mirror of
https://github.com/ether/etherpad-lite.git
synced 2026-05-04 19:56:37 +02:00
Merge branch 'develop'
This commit is contained in:
commit
913d460238
@ -36,7 +36,7 @@ jobs:
|
||||
- uses: pnpm/action-setup@v4
|
||||
name: Install pnpm
|
||||
with:
|
||||
version: 9.0.4
|
||||
version: 10
|
||||
run_install: false
|
||||
- name: Get pnpm store directory
|
||||
shell: bash
|
||||
@ -53,7 +53,7 @@ jobs:
|
||||
run: pnpm config set auto-install-peers false
|
||||
-
|
||||
name: Install libreoffice
|
||||
uses: awalsh128/cache-apt-pkgs-action@v1.5.0
|
||||
uses: awalsh128/cache-apt-pkgs-action@v1.5.1
|
||||
with:
|
||||
packages: libreoffice libreoffice-pdfimport
|
||||
version: 1.0
|
||||
@ -96,7 +96,7 @@ jobs:
|
||||
- uses: pnpm/action-setup@v4
|
||||
name: Install pnpm
|
||||
with:
|
||||
version: 9.0.4
|
||||
version: 10
|
||||
run_install: false
|
||||
- name: Get pnpm store directory
|
||||
shell: bash
|
||||
@ -113,7 +113,7 @@ jobs:
|
||||
run: pnpm config set auto-install-peers false
|
||||
-
|
||||
name: Install libreoffice
|
||||
uses: awalsh128/cache-apt-pkgs-action@v1.5.0
|
||||
uses: awalsh128/cache-apt-pkgs-action@v1.5.1
|
||||
with:
|
||||
packages: libreoffice libreoffice-pdfimport
|
||||
version: 1.0
|
||||
@ -168,7 +168,7 @@ jobs:
|
||||
- uses: pnpm/action-setup@v4
|
||||
name: Install pnpm
|
||||
with:
|
||||
version: 9.0.4
|
||||
version: 10
|
||||
run_install: false
|
||||
- name: Get pnpm store directory
|
||||
shell: bash
|
||||
@ -225,7 +225,7 @@ jobs:
|
||||
- uses: pnpm/action-setup@v4
|
||||
name: Install pnpm
|
||||
with:
|
||||
version: 9.0.4
|
||||
version: 10
|
||||
run_install: false
|
||||
- name: Get pnpm store directory
|
||||
shell: bash
|
||||
@ -38,7 +38,7 @@ jobs:
|
||||
- uses: pnpm/action-setup@v4
|
||||
name: Install pnpm
|
||||
with:
|
||||
version: 9.0.4
|
||||
version: 10
|
||||
run_install: false
|
||||
- name: Get pnpm store directory
|
||||
shell: bash
|
||||
@ -50,7 +50,7 @@ jobs:
|
||||
- uses: pnpm/action-setup@v4
|
||||
name: Install pnpm
|
||||
with:
|
||||
version: 9.0.4
|
||||
version: 10
|
||||
run_install: false
|
||||
- name: Get pnpm store directory
|
||||
shell: bash
|
||||
@ -36,7 +36,7 @@ jobs:
|
||||
- uses: pnpm/action-setup@v4
|
||||
name: Install pnpm
|
||||
with:
|
||||
version: 9.0.4
|
||||
version: 10
|
||||
run_install: false
|
||||
- name: Get pnpm store directory
|
||||
shell: bash
|
||||
@ -30,7 +30,7 @@ jobs:
|
||||
- uses: pnpm/action-setup@v4
|
||||
name: Install pnpm
|
||||
with:
|
||||
version: 9.0.4
|
||||
version: 10
|
||||
run_install: false
|
||||
- name: Get pnpm store directory
|
||||
shell: bash
|
||||
@ -103,7 +103,7 @@ jobs:
|
||||
- uses: pnpm/action-setup@v4
|
||||
name: Install pnpm
|
||||
with:
|
||||
version: 9.0.4
|
||||
version: 10
|
||||
run_install: false
|
||||
- name: Get pnpm store directory
|
||||
shell: bash
|
||||
@ -184,7 +184,7 @@ jobs:
|
||||
- uses: pnpm/action-setup@v4
|
||||
name: Install pnpm
|
||||
with:
|
||||
version: 9.0.4
|
||||
version: 10
|
||||
run_install: false
|
||||
- name: Get pnpm store directory
|
||||
shell: bash
|
||||
@ -32,7 +32,7 @@ jobs:
|
||||
- uses: pnpm/action-setup@v4
|
||||
name: Install pnpm
|
||||
with:
|
||||
version: 9.0.4
|
||||
version: 10
|
||||
run_install: false
|
||||
- name: Get pnpm store directory
|
||||
shell: bash
|
||||
@ -76,7 +76,7 @@ jobs:
|
||||
- uses: pnpm/action-setup@v4
|
||||
name: Install pnpm
|
||||
with:
|
||||
version: 9.0.4
|
||||
version: 10
|
||||
run_install: false
|
||||
- name: Get pnpm store directory
|
||||
shell: bash
|
||||
@ -147,7 +147,7 @@ jobs:
|
||||
- uses: pnpm/action-setup@v4
|
||||
name: Install pnpm
|
||||
with:
|
||||
version: 9.0.4
|
||||
version: 10
|
||||
run_install: false
|
||||
- name: Get pnpm store directory
|
||||
shell: bash
|
||||
@ -29,7 +29,7 @@ jobs:
|
||||
- uses: pnpm/action-setup@v4
|
||||
name: Install pnpm
|
||||
with:
|
||||
version: 9.0.4
|
||||
version: 10
|
||||
run_install: false
|
||||
- name: Get pnpm store directory
|
||||
shell: bash
|
||||
@ -32,7 +32,7 @@ jobs:
|
||||
- uses: pnpm/action-setup@v4
|
||||
name: Install pnpm
|
||||
with:
|
||||
version: 9.0.4
|
||||
version: 10
|
||||
run_install: false
|
||||
- name: Get pnpm store directory
|
||||
shell: bash
|
||||
@ -38,12 +38,12 @@ jobs:
|
||||
- uses: pnpm/action-setup@v4
|
||||
name: Install pnpm
|
||||
with:
|
||||
version: 9.0.4
|
||||
version: 10
|
||||
run_install: false
|
||||
- name: Only install direct dependencies
|
||||
run: pnpm config set auto-install-peers false
|
||||
- name: Install libreoffice
|
||||
uses: awalsh128/cache-apt-pkgs-action@v1.5.0
|
||||
uses: awalsh128/cache-apt-pkgs-action@v1.5.1
|
||||
with:
|
||||
packages: libreoffice libreoffice-pdfimport
|
||||
version: 1.0
|
||||
@ -62,7 +62,7 @@ jobs:
|
||||
run: pnpm config set auto-install-peers false
|
||||
-
|
||||
name: Install libreoffice
|
||||
uses: awalsh128/cache-apt-pkgs-action@v1.5.0
|
||||
uses: awalsh128/cache-apt-pkgs-action@v1.5.1
|
||||
with:
|
||||
packages: libreoffice libreoffice-pdfimport
|
||||
version: 1.0
|
||||
@ -39,7 +39,7 @@ jobs:
|
||||
- uses: pnpm/action-setup@v4
|
||||
name: Install pnpm
|
||||
with:
|
||||
version: 9.0.4
|
||||
version: 10
|
||||
run_install: false
|
||||
- name: Get pnpm store directory
|
||||
shell: bash
|
||||
83
.github/workflows/release.yml
vendored
Normal file
83
.github/workflows/release.yml
vendored
Normal file
@ -0,0 +1,83 @@
|
||||
name: Release etherpad
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
release_type:
|
||||
description: 'Choose the type of release to create'
|
||||
required: true
|
||||
default: 'patch'
|
||||
type: choice
|
||||
options:
|
||||
- patch
|
||||
- minor
|
||||
- major
|
||||
|
||||
jobs:
|
||||
releases:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: ether/etherpad-lite
|
||||
path: etherpad
|
||||
token: '${{ secrets.ETHER_RELEASE_TOKEN }}'
|
||||
fetch-depth: '0'
|
||||
fetch-tags: 'true'
|
||||
- name: Checkout master
|
||||
working-directory: etherpad
|
||||
run: |
|
||||
git fetch origin master
|
||||
git checkout master
|
||||
git reset --hard origin/master
|
||||
- name: Checkout develop
|
||||
working-directory: etherpad
|
||||
run: |
|
||||
git fetch origin develop
|
||||
git checkout develop
|
||||
git reset --hard origin/develop
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: ether/ether.github.com
|
||||
path: ether.github.com
|
||||
token: '${{ secrets.ETHER_RELEASE_TOKEN }}'
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
- uses: pnpm/action-setup@v4
|
||||
name: Install pnpm
|
||||
with:
|
||||
version: 10
|
||||
run_install: false
|
||||
- name: Get pnpm store directory
|
||||
shell: bash
|
||||
run: |
|
||||
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
|
||||
- name: Install dependencies etherpad
|
||||
run: pnpm install --frozen-lockfile
|
||||
working-directory: etherpad
|
||||
- name: Install dependencies ether.github.com
|
||||
run: pnpm install --frozen-lockfile
|
||||
working-directory: ether.github.com
|
||||
- name: Set git user
|
||||
run: |
|
||||
git config --global user.name "Etherpad Release Bot"
|
||||
git config --global user.email "noreply@etherpad.org"
|
||||
- uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
ruby-version: 2.7
|
||||
|
||||
- uses: reitzig/actions-asciidoctor@v2.0.2
|
||||
with:
|
||||
version: 2.0.18
|
||||
- name: Prepare release
|
||||
working-directory: etherpad
|
||||
run: |
|
||||
cd bin
|
||||
pnpm run release ${{ inputs.release_type }}
|
||||
- name: Push after release
|
||||
working-directory: etherpad
|
||||
run: |
|
||||
./bin/push-after-release.sh
|
||||
10
CHANGELOG.md
10
CHANGELOG.md
@ -1,3 +1,13 @@
|
||||
# 2.4.0
|
||||
|
||||
### Notable enhancements and fixes
|
||||
- Added home button to the pad panel. To show it in your current instance, please copy the updated "toolbar" settings from the `settings.json.template` file to your `settings.json` file.
|
||||
- Added more current design for the default collibri theme.
|
||||
- Added handling of recent visited pads in the collibri theme. You can now access the most recent three pads you visited in the pad panel.
|
||||
- Disable stats endpoints if enableMetrics is set to false. This allows you to disable the metrics endpoints if you don't want to use them.
|
||||
- Use Node LTS instead of always latest Node version.
|
||||
|
||||
|
||||
# 2.3.2
|
||||
|
||||
### Notable enhancements and fixes
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
# Author: muxator
|
||||
ARG BUILD_ENV=git
|
||||
|
||||
FROM node:alpine AS adminbuild
|
||||
FROM node:lts-alpine AS adminbuild
|
||||
RUN npm install -g pnpm@latest
|
||||
WORKDIR /opt/etherpad-lite
|
||||
COPY . .
|
||||
@ -13,7 +13,7 @@ RUN pnpm install
|
||||
RUN pnpm run build:ui
|
||||
|
||||
|
||||
FROM node:alpine AS build
|
||||
FROM node:lts-alpine AS build
|
||||
LABEL maintainer="Etherpad team, https://github.com/ether/etherpad-lite"
|
||||
|
||||
# Set these arguments when building the image from behind a proxy
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "admin",
|
||||
"private": true,
|
||||
"version": "2.3.2",
|
||||
"version": "2.4.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
@ -18,25 +18,25 @@
|
||||
"@radix-ui/react-toast": "^1.2.14",
|
||||
"@types/react": "^19.1.8",
|
||||
"@types/react-dom": "^19.1.6",
|
||||
"@typescript-eslint/eslint-plugin": "^8.34.0",
|
||||
"@typescript-eslint/parser": "^8.34.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.37.0",
|
||||
"@typescript-eslint/parser": "^8.37.0",
|
||||
"@vitejs/plugin-react-swc": "^3.10.2",
|
||||
"eslint": "^9.28.0",
|
||||
"eslint": "^9.31.0",
|
||||
"eslint-plugin-react-hooks": "^5.2.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.20",
|
||||
"i18next": "^25.2.1",
|
||||
"i18next": "^25.3.2",
|
||||
"i18next-browser-languagedetector": "^8.2.0",
|
||||
"lucide-react": "^0.515.0",
|
||||
"lucide-react": "^0.525.0",
|
||||
"react": "^19.1.0",
|
||||
"react-dom": "^19.1.0",
|
||||
"react-hook-form": "^7.57.0",
|
||||
"react-i18next": "^15.5.3",
|
||||
"react-router-dom": "^7.6.2",
|
||||
"react-hook-form": "^7.60.0",
|
||||
"react-i18next": "^15.6.0",
|
||||
"react-router-dom": "^7.6.3",
|
||||
"socket.io-client": "^4.8.1",
|
||||
"typescript": "^5.8.2",
|
||||
"vite": "^6.3.5",
|
||||
"vite-plugin-static-copy": "^3.0.0",
|
||||
"vite": "^7.0.4",
|
||||
"vite-plugin-static-copy": "^3.1.1",
|
||||
"vite-plugin-svgr": "^4.3.0",
|
||||
"zustand": "^5.0.5"
|
||||
"zustand": "^5.0.6"
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,22 +9,29 @@ export const ShoutPage = ()=>{
|
||||
const [message, setMessage] = useState<string>("");
|
||||
const [sticky, setSticky] = useState<boolean>(false);
|
||||
const socket = useStore(state => state.settingsSocket);
|
||||
const pluginSocket = useStore(state => state.pluginsSocket);
|
||||
const [shouts, setShouts] = useState<ShoutType[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
fetch('/stats')
|
||||
.then(response => response.json())
|
||||
.then(data => setTotalUsers(data.totalUsers));
|
||||
}, []);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
if(socket) {
|
||||
if(socket && pluginSocket) {
|
||||
console.log('Socket connected', socket.id);
|
||||
socket.on('shout', (shout) => {
|
||||
setShouts([...shouts, shout])
|
||||
})
|
||||
pluginSocket.on('results:stats', (statData) => {
|
||||
console.log('Shoutdata', statData);
|
||||
setTotalUsers(statData.totalUsers);
|
||||
})
|
||||
}
|
||||
}, [socket, shouts])
|
||||
}, [socket, shouts, pluginSocket])
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
if (pluginSocket) {
|
||||
pluginSocket.emit('getStats', {});
|
||||
}
|
||||
}, [pluginSocket]);
|
||||
|
||||
const sendMessage = () => {
|
||||
socket?.emit('shout', {
|
||||
|
||||
@ -28,10 +28,6 @@ export default defineConfig({
|
||||
'/admin-auth/': {
|
||||
target: 'http://localhost:9001',
|
||||
changeOrigin: true,
|
||||
},
|
||||
'/stats': {
|
||||
target: 'http://localhost:9001',
|
||||
changeOrigin: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
{
|
||||
"name": "bin",
|
||||
"version": "2.3.2",
|
||||
"version": "2.4.0",
|
||||
"description": "",
|
||||
"main": "checkAllPads.js",
|
||||
"directories": {
|
||||
"doc": "doc"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^1.8.4",
|
||||
"axios": "^1.10.0",
|
||||
"ep_etherpad-lite": "workspace:../src",
|
||||
"log4js": "^6.9.1",
|
||||
"semver": "^7.7.2",
|
||||
@ -15,7 +15,7 @@
|
||||
"ueberdb2": "^5.0.14"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^24.0.1",
|
||||
"@types/node": "^24.0.14",
|
||||
"@types/semver": "^7.7.0",
|
||||
"typescript": "^5.8.2"
|
||||
},
|
||||
|
||||
@ -29,7 +29,7 @@ jobs:
|
||||
- uses: pnpm/action-setup@v3
|
||||
name: Install pnpm
|
||||
with:
|
||||
version: 8
|
||||
version: 10
|
||||
run_install: false
|
||||
- name: Get pnpm store directory
|
||||
shell: bash
|
||||
|
||||
@ -18,7 +18,7 @@ jobs:
|
||||
- uses: pnpm/action-setup@v3
|
||||
name: Install pnpm
|
||||
with:
|
||||
version: 8
|
||||
version: 10
|
||||
run_install: false
|
||||
- name: Get pnpm store directory
|
||||
shell: bash
|
||||
|
||||
@ -21,7 +21,7 @@ jobs:
|
||||
- uses: pnpm/action-setup@v3
|
||||
name: Install pnpm
|
||||
with:
|
||||
version: 8
|
||||
version: 10
|
||||
run_install: false
|
||||
- name: Get pnpm store directory
|
||||
shell: bash
|
||||
|
||||
@ -56,6 +56,8 @@ const readJson = (filename: string) => JSON.parse(fs.readFileSync(filename, {enc
|
||||
const assertWorkDirClean = (opts:{
|
||||
cwd?: string;
|
||||
} = {}) => {
|
||||
// Stash any changes in the working directory so that we can check for modifications.
|
||||
runc('git stash')
|
||||
opts.cwd = runc('git rev-parse --show-cdup', opts) || cwd;
|
||||
const m = runc('git diff-files --name-status', opts);
|
||||
console.log(">"+m.trim()+"<")
|
||||
@ -172,8 +174,8 @@ try {
|
||||
console.log('Merging develop into master...');
|
||||
run('git merge --no-ff --no-edit develop');
|
||||
console.log(`Creating ${newVersion} tag...`);
|
||||
run(`git tag -s '${newVersion}' -m '${newVersion}'`);
|
||||
run(`git tag -s 'v${newVersion}' -m 'v${newVersion}'`);
|
||||
run(`git tag -a '${newVersion}' -m '${newVersion}'`);
|
||||
run(`git tag -a 'v${newVersion}' -m 'v${newVersion}'`);
|
||||
console.log('Switching back to develop...');
|
||||
run('git checkout develop');
|
||||
console.log('Merging master into develop...');
|
||||
|
||||
@ -1,10 +1,13 @@
|
||||
{
|
||||
"devDependencies": {
|
||||
"vitepress": "^1.6.3"
|
||||
"vitepress": "^2.0.0-alpha.9"
|
||||
},
|
||||
"scripts": {
|
||||
"docs:dev": "vitepress dev",
|
||||
"docs:build": "vitepress build",
|
||||
"docs:preview": "vitepress preview"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"search-insights": "^2.17.3"
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,5 +15,5 @@ You can choose a skin changing the parameter `skinName` in `settings.json`.
|
||||
|
||||
Since Etherpad **1.7.5**, two skins are included:
|
||||
|
||||
* `no-skin`: an empty skin, leaving the default Etherpad appearance unchanged, that you can use as a guidance to develop your own.
|
||||
* `no-skin`: an empty skin, leaving the default Etherpad appearance unchanged, that you can use as guidance to develop your own.
|
||||
* `colibris`: a new, experimental skin, that will become the default in Etherpad 2.0.
|
||||
|
||||
@ -50,6 +50,6 @@
|
||||
"type": "git",
|
||||
"url": "https://github.com/ether/etherpad-lite.git"
|
||||
},
|
||||
"version": "2.3.2",
|
||||
"version": "2.4.0",
|
||||
"license": "Apache-2.0"
|
||||
}
|
||||
|
||||
2364
pnpm-lock.yaml
generated
2364
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -171,6 +171,14 @@
|
||||
*/
|
||||
"showSettingsInAdminPage": "${SHOW_SETTINGS_IN_ADMIN_PAGE:true}",
|
||||
|
||||
/*
|
||||
* Enable/disable the metrics endpoint.
|
||||
*
|
||||
* This is used by the monitoring plugins to collect metrics about Etherpad.
|
||||
* If you do not use any monitoring plugins, you can disable this.
|
||||
*/
|
||||
"enableMetrics": "${ENABLE_METRICS:true}",
|
||||
|
||||
/*
|
||||
* Settings for cleanup of pads
|
||||
*/
|
||||
@ -642,7 +650,7 @@
|
||||
],
|
||||
"right": [
|
||||
["importexport", "timeslider", "savedrevision"],
|
||||
["settings", "embed"],
|
||||
["settings", "embed", "home"],
|
||||
["showusers"]
|
||||
],
|
||||
"timeslider": [
|
||||
|
||||
@ -162,6 +162,14 @@
|
||||
*/
|
||||
"showSettingsInAdminPage": true,
|
||||
|
||||
/*
|
||||
* Enable/disable the metrics endpoint.
|
||||
*
|
||||
* This is used by the monitoring plugins to collect metrics about Etherpad.
|
||||
* If you do not use any monitoring plugins, you can disable this.
|
||||
*/
|
||||
"enableMetrics": "${ENABLE_METRICS:true}",
|
||||
|
||||
/*
|
||||
* Settings for cleanup of pads
|
||||
*/
|
||||
@ -641,7 +649,7 @@
|
||||
],
|
||||
"right": [
|
||||
["importexport", "timeslider", "savedrevision"],
|
||||
["settings", "embed"],
|
||||
["settings", "embed", "home"],
|
||||
["showusers"]
|
||||
],
|
||||
"timeslider": [
|
||||
|
||||
@ -52,9 +52,16 @@
|
||||
"admin_settings.current_save.value": "Einstellungen speichern",
|
||||
"admin_settings.page-title": "Einstellungen - Etherpad",
|
||||
"index.newPad": "Neues Pad",
|
||||
"index.createOpenPad": "oder ein Pad mit folgendem Namen erstellen/öffnen:",
|
||||
"index.createOpenPad": "Pad öffnen",
|
||||
"index.openPad": "Öffne ein vorhandenes Pad mit folgendem Namen:",
|
||||
"pad.toolbar.bold.title": "Fett (Strg-B)",
|
||||
"index.recentPads": "Zuletzt bearbeitete Pads",
|
||||
"index.recentPadsEmpty": "Keine kürzlich bearbeiteten Pads gefunden.",
|
||||
"index.generateNewPad": "Neues Pad generieren",
|
||||
"index.labelPad": "Padname (optional)",
|
||||
"index.placeholderPadEnter": "Gib den Namen des Pads ein...",
|
||||
"index.createAndShareDocuments": "Erstelle und teile Dokumente in Echtzeit",
|
||||
"index.createAndShareDocumentsDescription": "Etherpad ermöglicht die gemeinsame Bearbeitung von Dokumenten in Echtzeit, ähnlich wie ein Live-Multiplayer-Editor, der in Ihrem Browser läuft.",
|
||||
"pad.toolbar.bold.title": "Fett (Strg-B)",
|
||||
"pad.toolbar.italic.title": "Kursiv (Strg-I)",
|
||||
"pad.toolbar.underline.title": "Unterstrichen (Strg-U)",
|
||||
"pad.toolbar.strikethrough.title": "Durchgestrichen (Strg+5)",
|
||||
@ -70,6 +77,7 @@
|
||||
"pad.toolbar.savedRevision.title": "Version speichern",
|
||||
"pad.toolbar.settings.title": "Einstellungen",
|
||||
"pad.toolbar.embed.title": "Dieses Pad teilen oder einbetten",
|
||||
"pad.toolbar.home.title": "Zurück zur Startseite",
|
||||
"pad.toolbar.showusers.title": "Benutzer dieses Pads anzeigen",
|
||||
"pad.colorpicker.save": "Speichern",
|
||||
"pad.colorpicker.cancel": "Abbrechen",
|
||||
|
||||
@ -34,8 +34,16 @@
|
||||
"admin_settings.page-title": "Settings - Etherpad",
|
||||
|
||||
"index.newPad": "New Pad",
|
||||
"index.createOpenPad": "or create/open a Pad with the name:",
|
||||
"index.createOpenPad": "Open pad by name",
|
||||
"index.openPad": "open an existing Pad with the name:",
|
||||
"index.recentPads": "Recent Pads",
|
||||
"index.recentPadsEmpty": "No recent pads found.",
|
||||
"index.generateNewPad": "Generate random pad name",
|
||||
"index.labelPad": "Pad name (optional)",
|
||||
"index.placeholderPadEnter": "Gib den Namen des Pads ein...",
|
||||
"index.createAndShareDocuments": "Create and share documents in real time",
|
||||
"index.createAndShareDocumentsDescription": "Etherpad allows you to edit documents collaboratively in real-time, much like a live multi-player editor that runs in your browser.",
|
||||
|
||||
|
||||
"pad.toolbar.bold.title": "Bold (Ctrl+B)",
|
||||
"pad.toolbar.italic.title": "Italic (Ctrl+I)",
|
||||
@ -53,6 +61,7 @@
|
||||
"pad.toolbar.savedRevision.title": "Save Revision",
|
||||
"pad.toolbar.settings.title": "Settings",
|
||||
"pad.toolbar.embed.title": "Share and Embed this pad",
|
||||
"pad.toolbar.home.title": "Back to home",
|
||||
"pad.toolbar.showusers.title": "Show the users on this pad",
|
||||
|
||||
"pad.colorpicker.save": "Save",
|
||||
|
||||
@ -14,6 +14,7 @@
|
||||
"Macofe",
|
||||
"MartaEgea",
|
||||
"Mklehr",
|
||||
"Ovruni",
|
||||
"Rubenwap",
|
||||
"Tiberius1701",
|
||||
"VegaDark",
|
||||
@ -50,11 +51,13 @@
|
||||
"admin_settings": "Configuración",
|
||||
"admin_settings.current": "Configuración actual",
|
||||
"admin_settings.current_example-devel": "Plantilla de ejemplo de configuración de desarrollo",
|
||||
"admin_settings.current_example-prod": "Ejemplo de plantilla de configuración de producción",
|
||||
"admin_settings.current_restart.value": "Reiniciar Etherpad",
|
||||
"admin_settings.current_save.value": "Guardar configuración",
|
||||
"admin_settings.page-title": "Configuración. Etherpad",
|
||||
"index.newPad": "Nuevo pad",
|
||||
"index.createOpenPad": "o crea/abre un pad con el nombre:",
|
||||
"index.openPad": "abrir un pad existente con el nombre:",
|
||||
"pad.toolbar.bold.title": "Negrita (Ctrl-B)",
|
||||
"pad.toolbar.italic.title": "Cursiva (Ctrl-I)",
|
||||
"pad.toolbar.underline.title": "Subrayado (Ctrl-U)",
|
||||
@ -87,6 +90,8 @@
|
||||
"pad.settings.fontType": "Tipografía:",
|
||||
"pad.settings.fontType.normal": "Normal",
|
||||
"pad.settings.language": "Idioma:",
|
||||
"pad.settings.deletePad": "Eliminar pad",
|
||||
"pad.delete.confirm": "¿De verdad quieres borrar este pad?",
|
||||
"pad.settings.about": "Acerca de",
|
||||
"pad.settings.poweredBy": "Funciona con",
|
||||
"pad.importExport.import_export": "Importar/Exportar",
|
||||
@ -123,6 +128,10 @@
|
||||
"pad.modals.corruptPad.cause": "Esto puede deberse a una mala configuración del servidor o algún otro comportamiento inesperado. Contacta con el administrador del servicio.",
|
||||
"pad.modals.deleted": "Borrado.",
|
||||
"pad.modals.deleted.explanation": "Este pad ha sido borrado.",
|
||||
"pad.modals.rateLimited": "Límite de solicitudes.",
|
||||
"pad.modals.rateLimited.explanation": "Enviaste demasiados mensajes a este pad por lo que te desconectó.",
|
||||
"pad.modals.rejected.explanation": "El servidor rechazó un mensaje que envió tu navegador.",
|
||||
"pad.modals.rejected.cause": "Es posible que el servidor se haya actualizado mientras veías el panel, o que haya un error en Etherpad. Intenta recargar la página.",
|
||||
"pad.modals.disconnected": "Te has desconectado.",
|
||||
"pad.modals.disconnected.explanation": "Se perdió la conexión con el servidor",
|
||||
"pad.modals.disconnected.cause": "El servidor podría no estar disponible. Contacta con el administrador del servicio si esto continúa sucediendo.",
|
||||
@ -135,6 +144,7 @@
|
||||
"pad.chat.loadmessages": "Cargar más mensajes",
|
||||
"pad.chat.stick.title": "Ampliar",
|
||||
"pad.chat.writeMessage.placeholder": "Enviar un mensaje",
|
||||
"timeslider.followContents": "Sigue las actualizaciones de contenido del pad",
|
||||
"timeslider.pageTitle": "{{appTitle}} Línea de tiempo",
|
||||
"timeslider.toolbar.returnbutton": "Volver al pad",
|
||||
"timeslider.toolbar.authors": "Autores:",
|
||||
@ -173,5 +183,6 @@
|
||||
"pad.impexp.uploadFailed": "El envío falló. Inténtalo de nuevo.",
|
||||
"pad.impexp.importfailed": "Fallo al importar",
|
||||
"pad.impexp.copypaste": "Intenta copiar y pegar",
|
||||
"pad.impexp.exportdisabled": "La exportación al formato {{type}} está desactivada. Contacta con tu administrador del sistema."
|
||||
"pad.impexp.exportdisabled": "La exportación al formato {{type}} está desactivada. Contacta con tu administrador del sistema.",
|
||||
"pad.impexp.maxFileSize": "El archivo es demasiado grande. Contacte al administrador del sitio para aumentar el tamaño de archivo permitido para la importación."
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
"BMRG14",
|
||||
"Beginneruser",
|
||||
"Dalba",
|
||||
"Darafsh",
|
||||
"Ebrahim",
|
||||
"Ebraminio",
|
||||
"FarsiNevis",
|
||||
@ -66,6 +67,7 @@
|
||||
"pad.toolbar.savedRevision.title": "ذخیرهسازی نسخه",
|
||||
"pad.toolbar.settings.title": "تنظیمات",
|
||||
"pad.toolbar.embed.title": "اشتراک و جاسازی این دفترچه یادداشت",
|
||||
"pad.toolbar.home.title": "بازگشت به صفحهٔ اصلی",
|
||||
"pad.toolbar.showusers.title": "نمایش کاربران در این دفترچه یادداشت",
|
||||
"pad.colorpicker.save": "ذخیره",
|
||||
"pad.colorpicker.cancel": "لغو",
|
||||
|
||||
@ -50,11 +50,13 @@
|
||||
"admin_settings": "Asetukset",
|
||||
"admin_settings.current": "Nykyinen kokoonpano",
|
||||
"admin_settings.current_example-devel": "Esimerkki kehitysasetusten mallista",
|
||||
"admin_settings.current_example-prod": "Esimerkkipohja tuotantoasetuksille",
|
||||
"admin_settings.current_restart.value": "Käynnistä Etherpad uudelleen",
|
||||
"admin_settings.current_save.value": "Tallenna asetukset",
|
||||
"admin_settings.page-title": "asetukset - Etherpad",
|
||||
"index.newPad": "Uusi muistio",
|
||||
"index.createOpenPad": "tai luo tai avaa muistio nimellä:",
|
||||
"index.openPad": "avaa olemassa oleva muistio nimellä:",
|
||||
"pad.toolbar.bold.title": "Lihavointi (Ctrl-B)",
|
||||
"pad.toolbar.italic.title": "Kursivointi (Ctrl-I)",
|
||||
"pad.toolbar.underline.title": "Alleviivaus (Ctrl-U)",
|
||||
@ -71,6 +73,7 @@
|
||||
"pad.toolbar.savedRevision.title": "Tallenna muutos",
|
||||
"pad.toolbar.settings.title": "Asetukset",
|
||||
"pad.toolbar.embed.title": "Jaa ja upota muistio",
|
||||
"pad.toolbar.home.title": "Takaisin kotiin",
|
||||
"pad.toolbar.showusers.title": "Näytä muistion käyttäjät",
|
||||
"pad.colorpicker.save": "Tallenna",
|
||||
"pad.colorpicker.cancel": "Peru",
|
||||
@ -137,6 +140,7 @@
|
||||
"pad.chat.loadmessages": "Lataa lisää viestejä",
|
||||
"pad.chat.stick.title": "Liimaa chatti ruutuun",
|
||||
"pad.chat.writeMessage.placeholder": "Kirjoita viestisi tähän",
|
||||
"timeslider.followContents": "Seuraa muistion sisällön päivityksiä",
|
||||
"timeslider.pageTitle": "{{appTitle}} -aikajana",
|
||||
"timeslider.toolbar.returnbutton": "Palaa muistioon",
|
||||
"timeslider.toolbar.authors": "Tekijät:",
|
||||
@ -175,5 +179,6 @@
|
||||
"pad.impexp.uploadFailed": "Lähetys epäonnistui. Yritä uudelleen.",
|
||||
"pad.impexp.importfailed": "Tuonti epäonnistui",
|
||||
"pad.impexp.copypaste": "Kopioi ja liitä",
|
||||
"pad.impexp.exportdisabled": "Vienti muotoon \"{{type}}\" ei ole käytössä. Ota yhteys ylläpitäjään saadaksesi lisätietoja."
|
||||
"pad.impexp.exportdisabled": "Vienti muotoon \"{{type}}\" ei ole käytössä. Ota yhteys ylläpitäjään saadaksesi lisätietoja.",
|
||||
"pad.impexp.maxFileSize": "Tiedosto on liian suuri. Ota yhteyttä sivustosi ylläpitäjään ja pyydä heitä korottomaan suurinta sallittua tiedostokokoa."
|
||||
}
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
"C13m3n7",
|
||||
"Cquoi",
|
||||
"Crochet.david",
|
||||
"Derugon",
|
||||
"Envlh",
|
||||
"Framafan",
|
||||
"Fylip22",
|
||||
@ -83,6 +84,7 @@
|
||||
"pad.toolbar.savedRevision.title": "Enregistrer la révision",
|
||||
"pad.toolbar.settings.title": "Paramètres",
|
||||
"pad.toolbar.embed.title": "Partager et intégrer ce bloc-notes",
|
||||
"pad.toolbar.home.title": "Retour à l’accueil",
|
||||
"pad.toolbar.showusers.title": "Afficher les utilisateurs du bloc-notes",
|
||||
"pad.colorpicker.save": "Enregistrer",
|
||||
"pad.colorpicker.cancel": "Annuler",
|
||||
|
||||
@ -58,6 +58,7 @@
|
||||
"pad.toolbar.savedRevision.title": "Gardar a revisión",
|
||||
"pad.toolbar.settings.title": "Axustes",
|
||||
"pad.toolbar.embed.title": "Compartir e incorporar este documento",
|
||||
"pad.toolbar.home.title": "Volver ao inicio",
|
||||
"pad.toolbar.showusers.title": "Mostrar as usuarias deste documento",
|
||||
"pad.colorpicker.save": "Gardar",
|
||||
"pad.colorpicker.cancel": "Cancelar",
|
||||
@ -120,7 +121,7 @@
|
||||
"pad.modals.disconnected.explanation": "Perdeuse a conexión co servidor",
|
||||
"pad.modals.disconnected.cause": "O servidor non está dispoñible. Póñase en contacto co administrador do servizo se o problema continúa.",
|
||||
"pad.share": "Compartir este documento",
|
||||
"pad.share.readonly": "Só lectura",
|
||||
"pad.share.readonly": "Lectura só",
|
||||
"pad.share.link": "Ligazón",
|
||||
"pad.share.emebdcode": "Incorporar o URL",
|
||||
"pad.chat": "Chat",
|
||||
|
||||
@ -10,7 +10,8 @@
|
||||
"Notramo",
|
||||
"Ovari",
|
||||
"R-Joe",
|
||||
"Tgr"
|
||||
"Tgr",
|
||||
"Urbalazs"
|
||||
]
|
||||
},
|
||||
"admin.page-title": "Admin irányítópult - Etherpad",
|
||||
@ -67,7 +68,7 @@
|
||||
"pad.toolbar.embed.title": "Jegyzetfüzet beágyazása és megosztása",
|
||||
"pad.toolbar.showusers.title": "Jegyzetfüzet felhasználóinak megmutatása",
|
||||
"pad.colorpicker.save": "Mentés",
|
||||
"pad.colorpicker.cancel": "Mégsem",
|
||||
"pad.colorpicker.cancel": "Mégse",
|
||||
"pad.loading": "Betöltés…",
|
||||
"pad.noCookie": "Nem található a süti. Engedélyezd a böngésződben a sütik használatát! A munkamenet és a beállítások nem kerülnek mentésre a látogatások között. Ennek oka lehet az, hogy az Etherpad egyes böngészőkben szerepel az iFrame-ben. Ellenőrizze, hogy az Etherpad ugyanabban az altartomány / tartományban van-e, mint a szülő iFrame",
|
||||
"pad.permissionDenied": "Nincs engedélyed ezen jegyzetfüzet eléréséhez",
|
||||
|
||||
@ -56,6 +56,7 @@
|
||||
"pad.toolbar.savedRevision.title": "Version salveguardate",
|
||||
"pad.toolbar.settings.title": "Configuration",
|
||||
"pad.toolbar.embed.title": "Condivider e incorporar iste pad",
|
||||
"pad.toolbar.home.title": "Retro al initio",
|
||||
"pad.toolbar.showusers.title": "Monstrar le usatores de iste pad",
|
||||
"pad.colorpicker.save": "Salveguardar",
|
||||
"pad.colorpicker.cancel": "Cancellar",
|
||||
|
||||
@ -42,6 +42,7 @@
|
||||
"pad.toolbar.savedRevision.title": "Versione salvata",
|
||||
"pad.toolbar.settings.title": "Impostazioni",
|
||||
"pad.toolbar.embed.title": "Condividi ed incorpora questo Pad",
|
||||
"pad.toolbar.home.title": "Torna alla pagina principale",
|
||||
"pad.toolbar.showusers.title": "Visualizza gli utenti su questo Pad",
|
||||
"pad.colorpicker.save": "Salva",
|
||||
"pad.colorpicker.cancel": "Annulla",
|
||||
|
||||
@ -58,6 +58,7 @@
|
||||
"pad.toolbar.savedRevision.title": "Зачувај преработка",
|
||||
"pad.toolbar.settings.title": "Поставки",
|
||||
"pad.toolbar.embed.title": "Споделете и вметнете ја тетраткава",
|
||||
"pad.toolbar.home.title": "Назад на Почетна",
|
||||
"pad.toolbar.showusers.title": "Прикажи корисниците на тетраткава",
|
||||
"pad.colorpicker.save": "Зачувај",
|
||||
"pad.colorpicker.cancel": "Откажи",
|
||||
|
||||
@ -112,7 +112,7 @@
|
||||
"pad.savedrevs.marked": "Semakan ini telah ditandai sebagai semakan tersimpan",
|
||||
"pad.savedrevs.timeslider": "Anda boleh melihat semakan yang tersimpan dengan melawat gelangsar masa",
|
||||
"pad.userlist.entername": "Taipkan nama anda",
|
||||
"pad.userlist.unnamed": "tanpa nama",
|
||||
"pad.userlist.unnamed": "tidak bernama",
|
||||
"pad.editbar.clearcolors": "Padamkan warna pengarang pada seluruh dokumen?",
|
||||
"pad.impexp.importbutton": "Import Sekarang",
|
||||
"pad.impexp.importing": "Sedang mengimport...",
|
||||
|
||||
@ -12,6 +12,7 @@
|
||||
]
|
||||
},
|
||||
"admin_plugins.description": "विवरण",
|
||||
"admin_plugins.name": "नाम",
|
||||
"index.newPad": "नयाँ प्याड",
|
||||
"index.createOpenPad": "नाम सहितको नयाँ प्याड सिर्जना गर्ने / खोल्ने :",
|
||||
"pad.toolbar.bold.title": "मोटो (Ctrl-B)",
|
||||
|
||||
@ -56,6 +56,7 @@
|
||||
"pad.toolbar.savedRevision.title": "Argistré la revision",
|
||||
"pad.toolbar.settings.title": "Paràmeter",
|
||||
"pad.toolbar.embed.title": "Partagé e antëgré ës feuj",
|
||||
"pad.toolbar.home.title": "Artorn a l'intrada",
|
||||
"pad.toolbar.showusers.title": "Smon-e j'utent ansima a 's feuj",
|
||||
"pad.colorpicker.save": "Argistré",
|
||||
"pad.colorpicker.cancel": "Anulé",
|
||||
|
||||
@ -1,11 +1,12 @@
|
||||
{
|
||||
"@metadata": {
|
||||
"authors": [
|
||||
"Ahmed-Najib-Biabani-Ibrahimkhel"
|
||||
"Ahmed-Najib-Biabani-Ibrahimkhel",
|
||||
"شاه زمان پټان"
|
||||
]
|
||||
},
|
||||
"index.newPad": "نوې ليکچه",
|
||||
"index.createOpenPad": "يا په همدې نوم يوه نوې ليکچه جوړول/پرانيستل:",
|
||||
"index.createOpenPad": "يا په همدې نوم يوه نوې ليکچه جوړول/پرانېستل:",
|
||||
"pad.toolbar.bold.title": "زغرد (Ctrl-B)",
|
||||
"pad.toolbar.italic.title": "رېوند (Ctrl-I)",
|
||||
"pad.toolbar.underline.title": "لرکرښن (Ctrl+U)",
|
||||
@ -16,11 +17,12 @@
|
||||
"pad.toolbar.redo.title": "بياکړل (Ctrl-Y)",
|
||||
"pad.toolbar.clearAuthorship.title": "د ليکوالۍ رنگونه سپينول (Ctrl+Shift+C)",
|
||||
"pad.toolbar.savedRevision.title": "مخکتنه خوندي کول",
|
||||
"pad.toolbar.settings.title": "امستنې",
|
||||
"pad.toolbar.settings.title": "اوڼنې",
|
||||
"pad.toolbar.home.title": "بېرته کور ته",
|
||||
"pad.colorpicker.save": "خوندي کول",
|
||||
"pad.colorpicker.cancel": "ناگارل",
|
||||
"pad.loading": "رابرسېرېږي...",
|
||||
"pad.settings.padSettings": "د ليکچې امستنې",
|
||||
"pad.settings.padSettings": "د ليکچې اوڼنې",
|
||||
"pad.settings.myView": "زما کتنه",
|
||||
"pad.settings.stickychat": "تل په پردې بانډار کول",
|
||||
"pad.settings.chatandusers": "کارنان او بانډار ښکاره کول",
|
||||
|
||||
@ -71,6 +71,7 @@
|
||||
"pad.toolbar.savedRevision.title": "Сохранить версию",
|
||||
"pad.toolbar.settings.title": "Настройки",
|
||||
"pad.toolbar.embed.title": "Поделиться и встроить этот документ",
|
||||
"pad.toolbar.home.title": "Вернуться в начало",
|
||||
"pad.toolbar.showusers.title": "Показать пользователей в документе",
|
||||
"pad.colorpicker.save": "Сохранить",
|
||||
"pad.colorpicker.cancel": "Отмена",
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
{
|
||||
"@metadata": {
|
||||
"authors": [
|
||||
"Conquistador",
|
||||
"Vlad5250"
|
||||
"Winston Sung"
|
||||
]
|
||||
},
|
||||
"admin_plugins.available_not-found": "Nijedan plugin nije pronađen.",
|
||||
@ -84,6 +84,8 @@
|
||||
"pad.settings.fontType": "Тип шрифту:",
|
||||
"pad.settings.fontType.normal": "Звичайний",
|
||||
"pad.settings.language": "Мова:",
|
||||
"pad.settings.deletePad": "Вилучити документ",
|
||||
"pad.delete.confirm": "Ви дійсно хочете вилучити цей документ?",
|
||||
"pad.settings.about": "Про програму",
|
||||
"pad.settings.poweredBy": "Працює на",
|
||||
"pad.importExport.import_export": "Імпорт/Експорт",
|
||||
|
||||
@ -66,6 +66,7 @@
|
||||
"pad.toolbar.savedRevision.title": "儲存修訂版",
|
||||
"pad.toolbar.settings.title": "設定",
|
||||
"pad.toolbar.embed.title": "分享和嵌入此記事本",
|
||||
"pad.toolbar.home.title": "返回首頁",
|
||||
"pad.toolbar.showusers.title": "顯示此記事本的使用者",
|
||||
"pad.colorpicker.save": "儲存",
|
||||
"pad.colorpicker.cancel": "取消",
|
||||
|
||||
@ -1,13 +1,15 @@
|
||||
'use strict';
|
||||
// @ts-nocheck
|
||||
|
||||
|
||||
const DB = require('./DB');
|
||||
const Store = require('@etherpad/express-session').Store;
|
||||
import expressSession from 'express-session'
|
||||
|
||||
const log4js = require('log4js');
|
||||
const util = require('util');
|
||||
|
||||
const logger = log4js.getLogger('SessionStore');
|
||||
|
||||
class SessionStore extends Store {
|
||||
class SessionStore extends expressSession.Store {
|
||||
/**
|
||||
* @param {?number} [refresh] - How often (in milliseconds) `touch()` will update a session's
|
||||
* database record with the cookie's latest expiration time. If the difference between the
|
||||
@ -19,7 +21,7 @@ class SessionStore extends Store {
|
||||
* Etherpad is restarted. Use `null` to prevent `touch()` from ever updating the record.
|
||||
* Ignored if the cookie does not expire.
|
||||
*/
|
||||
constructor(refresh = null) {
|
||||
constructor(refresh: number | null = null) {
|
||||
super();
|
||||
this._refresh = refresh;
|
||||
// Maps session ID to an object with the following properties:
|
||||
|
||||
@ -4,12 +4,10 @@ import {Socket} from "node:net";
|
||||
import type {MapArrayType} from "../types/MapType";
|
||||
|
||||
import _ from 'underscore';
|
||||
// @ts-ignore
|
||||
import cookieParser from 'cookie-parser';
|
||||
import events from 'events';
|
||||
import express from 'express';
|
||||
// @ts-ignore
|
||||
import expressSession from '@etherpad/express-session';
|
||||
import expressSession, {Store} from 'express-session';
|
||||
import fs from 'fs';
|
||||
const hooks = require('../../static/js/pluginfw/hooks');
|
||||
import log4js from 'log4js';
|
||||
@ -24,7 +22,7 @@ import SecretRotator from '../security/SecretRotator';
|
||||
let secretRotator: SecretRotator|null = null;
|
||||
const logger = log4js.getLogger('http');
|
||||
let serverName:string;
|
||||
let sessionStore: { shutdown: () => void; } | null;
|
||||
let sessionStore: Store | null;
|
||||
const sockets:Set<Socket> = new Set();
|
||||
const socketsEvents = new events.EventEmitter();
|
||||
const startTime = stats.settableGauge('httpStartTime');
|
||||
@ -59,6 +57,7 @@ const closeServer = async () => {
|
||||
startTime.setValue(0);
|
||||
logger.info('HTTP server closed');
|
||||
}
|
||||
// @ts-ignore
|
||||
if (sessionStore) sessionStore.shutdown();
|
||||
sessionStore = null;
|
||||
if (secretRotator) secretRotator.stop();
|
||||
@ -198,10 +197,9 @@ exports.restartServer = async () => {
|
||||
|
||||
sessionStore = new SessionStore(settings.cookie.sessionRefreshInterval);
|
||||
exports.sessionMiddleware = expressSession({
|
||||
propagateTouch: true,
|
||||
rolling: true,
|
||||
secret,
|
||||
store: sessionStore,
|
||||
store: sessionStore ?? undefined,
|
||||
resave: false,
|
||||
saveUninitialized: false,
|
||||
// Set the cookie name to a javascript identifier compatible string. Makes code handling it
|
||||
@ -234,7 +232,7 @@ exports.restartServer = async () => {
|
||||
// Give plugins an opportunity to install handlers/middleware before the express-session
|
||||
// middleware. This allows plugins to avoid creating an express-session record in the database
|
||||
// when it is not needed (e.g., public static content).
|
||||
await hooks.aCallAll('expressPreSession', {app});
|
||||
await hooks.aCallAll('expressPreSession', {app, settings});
|
||||
app.use(exports.sessionMiddleware);
|
||||
|
||||
app.use(webaccess.checkAccess);
|
||||
|
||||
@ -39,6 +39,11 @@ exports.socketio = (hookName:string, args:ArgsExpressType, cb:Function) => {
|
||||
})
|
||||
}
|
||||
|
||||
socket.on('getStats', ()=>{
|
||||
console.log("Getting stats for admin plugins");
|
||||
socket.emit('results:stats', require('../../stats').toJSON());
|
||||
})
|
||||
|
||||
socket.on('getInstalled', async (query: string) => {
|
||||
// send currently installed plugins
|
||||
const installed =
|
||||
@ -53,6 +58,7 @@ exports.socketio = (hookName:string, args:ArgsExpressType, cb:Function) => {
|
||||
socket.emit('results:installed', {installed});
|
||||
});
|
||||
|
||||
|
||||
socket.on('checkUpdates', async () => {
|
||||
// Check plugins for updates
|
||||
try {
|
||||
|
||||
@ -1,16 +1,39 @@
|
||||
'use strict';
|
||||
|
||||
import express from "express";
|
||||
|
||||
const log4js = require('log4js');
|
||||
const clientLogger = log4js.getLogger('client');
|
||||
const {Formidable} = require('formidable');
|
||||
const apiHandler = require('../../handler/APIHandler');
|
||||
const util = require('util');
|
||||
|
||||
|
||||
function objectAsString(obj: any): string {
|
||||
let output = '';
|
||||
for (const property in obj) {
|
||||
if(obj.hasOwnProperty(property) && typeof obj[property] !== 'function') {
|
||||
let value = obj[property];
|
||||
if(typeof value === 'object' && !Array.isArray(value) && value !== null) {
|
||||
value = '{' + objectAsString(value) + '}';
|
||||
}
|
||||
output += property + ': ' + value +'; ';
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
exports.expressPreSession = async (hookName:string, {app}:any) => {
|
||||
app.use(express.json());
|
||||
// The Etherpad client side sends information about how a disconnect happened
|
||||
app.post('/ep/pad/connection-diagnostic-info', async (req:any, res:any) => {
|
||||
const [fields, files] = await (new Formidable({})).parse(req);
|
||||
clientLogger.info(`DIAGNOSTIC-INFO: ${fields.diagnosticInfo}`);
|
||||
if (!req.body ||!req.body.diagnosticInfo || typeof req.body.diagnosticInfo !== 'object') {
|
||||
clientLogger.warn('DIAGNOSTIC-INFO: No diagnostic info provided');
|
||||
res.status(400).end('No diagnostic info provided');
|
||||
return;
|
||||
}
|
||||
|
||||
clientLogger.info(`DIAGNOSTIC-INFO: ${objectAsString(req.body.diagnosticInfo)}`);
|
||||
res.end('OK');
|
||||
});
|
||||
|
||||
|
||||
@ -20,7 +20,7 @@ exports.socketio = (hookName: string, {io}: any) => {
|
||||
}
|
||||
|
||||
|
||||
exports.expressPreSession = async (hookName:string, {app}:ArgsExpressType) => {
|
||||
exports.expressPreSession = async (hookName:string, {app, settings}:ArgsExpressType) => {
|
||||
// This endpoint is intended to conform to:
|
||||
// https://www.ietf.org/archive/id/draft-inadarei-api-health-check-06.html
|
||||
app.get('/health', (req:any, res:any) => {
|
||||
@ -31,9 +31,12 @@ exports.expressPreSession = async (hookName:string, {app}:ArgsExpressType) => {
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/stats', (req:any, res:any) => {
|
||||
res.json(require('../../stats').toJSON());
|
||||
});
|
||||
if (settings.enableMetrics) {
|
||||
app.get('/stats', (req:any, res:any) => {
|
||||
res.json(require('../../stats').toJSON());
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
app.get('/javascript', (req:any, res:any) => {
|
||||
res.send(eejs.require('ep_etherpad-lite/templates/javascript.html', {req}));
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
import {Express} from "express";
|
||||
import {MapArrayType} from "./MapType";
|
||||
|
||||
export type ArgsExpressType = {
|
||||
app:Express,
|
||||
io: any,
|
||||
server:any
|
||||
settings: MapArrayType<any>
|
||||
}
|
||||
|
||||
@ -205,6 +205,12 @@ exports.padOptions = {
|
||||
lang: null,
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Wether to enable the /stats endpoint. The functionality in the admin menu is untouched for this.
|
||||
*/
|
||||
exports.enableMetrics = true
|
||||
|
||||
/**
|
||||
* Whether certain shortcut keys are enabled for a user in the pad
|
||||
*/
|
||||
@ -245,7 +251,7 @@ exports.toolbar = {
|
||||
],
|
||||
right: [
|
||||
['importexport', 'timeslider', 'savedrevision'],
|
||||
['settings', 'embed'],
|
||||
['settings', 'embed', 'home'],
|
||||
['showusers'],
|
||||
],
|
||||
timeslider: [
|
||||
|
||||
@ -240,6 +240,7 @@ module.exports = {
|
||||
settings: defaultButtonAttributes('settings'),
|
||||
embed: defaultButtonAttributes('embed'),
|
||||
showusers: defaultButtonAttributes('showusers'),
|
||||
home: defaultButtonAttributes('home'),
|
||||
|
||||
timeslider_export: {
|
||||
command: 'import_export',
|
||||
|
||||
@ -30,23 +30,23 @@
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"@etherpad/express-session": "^1.18.4",
|
||||
"async": "^3.2.6",
|
||||
"axios": "^1.8.4",
|
||||
"axios": "^1.10.0",
|
||||
"cookie-parser": "^1.4.7",
|
||||
"cross-env": "^7.0.3",
|
||||
"cross-spawn": "^7.0.6",
|
||||
"ejs": "^3.1.10",
|
||||
"esbuild": "^0.25.5",
|
||||
"esbuild": "^0.25.8",
|
||||
"express": "4.21.2",
|
||||
"express-rate-limit": "^7.5.0",
|
||||
"express-rate-limit": "^8.0.0",
|
||||
"express-session": "^1.18.2",
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"find-root": "1.1.0",
|
||||
"formidable": "^3.5.4",
|
||||
"http-errors": "^2.0.0",
|
||||
"jose": "^5.10.0",
|
||||
"js-cookie": "^3.0.5",
|
||||
"jsdom": "^26.0.0",
|
||||
"jsdom": "^26.1.0",
|
||||
"jsonminify": "0.4.2",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"jwt-decode": "^4.0.0",
|
||||
@ -57,8 +57,8 @@
|
||||
"lru-cache": "^11.1.0",
|
||||
"measured-core": "^2.0.0",
|
||||
"mime-types": "^3.0.1",
|
||||
"oidc-provider": "^9.1.3",
|
||||
"openapi-backend": "^5.12.0",
|
||||
"oidc-provider": "^9.3.0",
|
||||
"openapi-backend": "^5.13.0",
|
||||
"proxy-addr": "^2.0.7",
|
||||
"rate-limiter-flexible": "^7.1.1",
|
||||
"rehype": "^13.0.2",
|
||||
@ -69,7 +69,7 @@
|
||||
"semver": "^7.7.2",
|
||||
"socket.io": "^4.8.1",
|
||||
"socket.io-client": "^4.8.1",
|
||||
"superagent": "10.2.1",
|
||||
"superagent": "10.2.2",
|
||||
"swagger-ui-express": "^5.0.1",
|
||||
"tinycon": "0.6.8",
|
||||
"tsx": "4.20.3",
|
||||
@ -83,19 +83,21 @@
|
||||
"etherpad-lite": "node/server.ts"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.53.0",
|
||||
"@playwright/test": "^1.54.1",
|
||||
"@types/async": "^3.2.24",
|
||||
"@types/cookie-parser": "^1.4.9",
|
||||
"@types/express": "^4.17.21",
|
||||
"@types/express-session": "^1.18.2",
|
||||
"@types/formidable": "^3.4.5",
|
||||
"@types/http-errors": "^2.0.5",
|
||||
"@types/jquery": "^3.5.32",
|
||||
"@types/js-cookie": "^3.0.6",
|
||||
"@types/jsdom": "^21.1.7",
|
||||
"@types/jsonwebtoken": "^9.0.9",
|
||||
"@types/jsonwebtoken": "^9.0.10",
|
||||
"@types/mime-types": "^3.0.1",
|
||||
"@types/mocha": "^10.0.9",
|
||||
"@types/node": "^24.0.1",
|
||||
"@types/oidc-provider": "^9.1.0",
|
||||
"@types/node": "^24.0.14",
|
||||
"@types/oidc-provider": "^9.1.1",
|
||||
"@types/semver": "^7.7.0",
|
||||
"@types/sinon": "^17.0.3",
|
||||
"@types/supertest": "^6.0.2",
|
||||
@ -103,19 +105,19 @@
|
||||
"@types/underscore": "^1.13.0",
|
||||
"@types/whatwg-mimetype": "^3.0.2",
|
||||
"chokidar": "^4.0.3",
|
||||
"eslint": "^9.28.0",
|
||||
"eslint": "^9.31.0",
|
||||
"eslint-config-etherpad": "^4.0.4",
|
||||
"etherpad-cli-client": "^3.0.2",
|
||||
"mocha": "^11.6.0",
|
||||
"etherpad-cli-client": "^3.0.4",
|
||||
"mocha": "^11.7.1",
|
||||
"mocha-froth": "^0.2.10",
|
||||
"nodeify": "^1.0.1",
|
||||
"openapi-schema-validation": "^0.4.2",
|
||||
"set-cookie-parser": "^2.7.1",
|
||||
"sinon": "^21.0.0",
|
||||
"split-grid": "^1.0.11",
|
||||
"supertest": "^7.1.1",
|
||||
"supertest": "^7.1.3",
|
||||
"typescript": "^5.8.2",
|
||||
"vitest": "^3.2.3"
|
||||
"vitest": "^3.2.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.18.2",
|
||||
@ -142,6 +144,6 @@
|
||||
"debug:socketio": "cross-env DEBUG=socket.io* node --require tsx/cjs node/server.ts",
|
||||
"test:vitest": "vitest"
|
||||
},
|
||||
"version": "2.3.2",
|
||||
"version": "2.4.0",
|
||||
"license": "Apache-2.0"
|
||||
}
|
||||
|
||||
@ -101,7 +101,7 @@
|
||||
.buttonicon-pencil-alt:before { content: '\e808'; } /* '' */
|
||||
.buttonicon-file-code:before { content: '\e809'; } /* '' */
|
||||
.buttonicon-mail:before { content: '\e80a'; } /* '' */
|
||||
.buttonicon-home:before { content: '\e80b'; } /* '' */
|
||||
.buttonicon-home:before { content: '\e80b'; font-size: 20px } /* '' */
|
||||
.buttonicon-trash:before { content: '\e80e'; } /* '' */
|
||||
.buttonicon-times:before { content: '\e826'; } /* '' */
|
||||
.buttonicon-pause:before { content: '\e829'; } /* '' */
|
||||
|
||||
@ -2526,7 +2526,15 @@ function Ace2Inner(editorInfo, cssManagers) {
|
||||
|
||||
const handleKeyEvent = (evt) => {
|
||||
if (!isEditable) return;
|
||||
const {type, charCode, keyCode, which, altKey, shiftKey} = evt;
|
||||
const {type, charCode, keyCode, which, shiftKey} = evt;
|
||||
|
||||
// If DOM3 support exists, ensure that the left ALT key was pressed. This
|
||||
// allows keyboard layouts with special meaning for right-alt-char to
|
||||
// continue working on Firefox / macOS.
|
||||
let altKey = evt.altKey;
|
||||
if (evt.originalEvent.location !== undefined) {
|
||||
altKey = altKey && evt.originalEvent.location === evt.originalEvent.DOM_KEY_LOCATION_LEFT;
|
||||
}
|
||||
|
||||
// Don't take action based on modifier keys going up and down.
|
||||
// Modifier keys do not generate "keypress" events.
|
||||
|
||||
@ -7,8 +7,6 @@ let language = document.cookie.match(/language=((\w{2,3})(-\w+)?)/);
|
||||
if (language) regexpLang = language[1];
|
||||
|
||||
html10n.mt.bind('indexed', () => {
|
||||
console.log('Navigator language', navigator.language)
|
||||
console.log('Localizing things', [regexpLang, navigator.language, 'en'])
|
||||
html10n.localize([regexpLang, navigator.language, 'en']);
|
||||
});
|
||||
|
||||
|
||||
@ -725,18 +725,18 @@ const pad = {
|
||||
}
|
||||
},
|
||||
asyncSendDiagnosticInfo: () => {
|
||||
window.setTimeout(() => {
|
||||
$.ajax(
|
||||
{
|
||||
type: 'post',
|
||||
url: '../ep/pad/connection-diagnostic-info',
|
||||
data: {
|
||||
diagnosticInfo: JSON.stringify(pad.diagnosticInfo),
|
||||
},
|
||||
success: () => {},
|
||||
error: () => {},
|
||||
});
|
||||
}, 0);
|
||||
const currentUrl = window.location.href;
|
||||
fetch('../ep/pad/connection-diagnostic-info', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
diagnosticInfo: pad.diagnosticInfo,
|
||||
}),
|
||||
}).catch((error) => {
|
||||
console.error('Error sending diagnostic info:', error);
|
||||
})
|
||||
},
|
||||
forceReconnect: () => {
|
||||
$('form#reconnectform input.padId').val(pad.getPadId());
|
||||
|
||||
@ -364,6 +364,9 @@ exports.padeditbar = new class {
|
||||
this.registerDropdownCommand('connectivity');
|
||||
this.registerDropdownCommand('import_export');
|
||||
this.registerDropdownCommand('embed');
|
||||
this.registerCommand('home', ()=>{
|
||||
window.location.href = window.location.href + "/../.."
|
||||
})
|
||||
|
||||
this.registerCommand('settings', () => {
|
||||
this.toggleDropDown('settings');
|
||||
|
||||
@ -489,6 +489,15 @@ const paduserlist = (() => {
|
||||
online++;
|
||||
}
|
||||
}
|
||||
const recentPadsList = JSON.parse(localStorage.getItem('recentPads'));
|
||||
const pathSegments = window.location.pathname.split('/');
|
||||
const padName = pathSegments[pathSegments.length - 1];
|
||||
const existingPad = recentPadsList.find((pad) => pad.name === padName);
|
||||
if (existingPad) {
|
||||
existingPad.members = online;
|
||||
}
|
||||
localStorage.setItem('recentPads', JSON.stringify(recentPadsList));
|
||||
|
||||
|
||||
$('#online_count').text(online);
|
||||
|
||||
|
||||
@ -16,7 +16,6 @@ class Scroll {
|
||||
this.outerWin = outerWin;
|
||||
this.doc = this.outerWin.contentDocument!;
|
||||
this.rootDocument = document;
|
||||
console.log(this.rootDocument)
|
||||
}
|
||||
|
||||
scrollWhenCaretIsInTheLastLineOfViewportWhenNecessary(rep: RepModel, isScrollableEvent: boolean, innerHeight: number) {
|
||||
|
||||
1
src/static/js/vendors/html10n.ts
vendored
1
src/static/js/vendors/html10n.ts
vendored
@ -472,7 +472,6 @@ export class Html10n {
|
||||
}
|
||||
|
||||
localize(langs: (string|undefined)[]|string) {
|
||||
console.log('Available langs ', langs)
|
||||
if ('string' === typeof langs) {
|
||||
langs = [langs];
|
||||
}
|
||||
|
||||
BIN
src/static/skins/colibris/images/Designlayout.png
Normal file
BIN
src/static/skins/colibris/images/Designlayout.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 58 KiB |
@ -1,88 +1,258 @@
|
||||
#button,
|
||||
body,
|
||||
form {
|
||||
border: none
|
||||
:root {
|
||||
--etherpad-color: #64d29b;
|
||||
--etherpad-color-dark: #4a5d5c;
|
||||
--etherpad-border: oklch(92.8% 0.006 264.531);
|
||||
--muted-text: oklch(44.6% 0.03 256.802);
|
||||
--muted-border: oklch(87.2% 0.01 258.338);
|
||||
--muted-background: hsl(240 4.8% 95.9%);
|
||||
--ep-color: rgb(22 163 74);
|
||||
--warm-green-olive: #7c9a3e;
|
||||
--warm-green-moss: #8fae4a;
|
||||
--warm-green-lime: #b7c96c;
|
||||
--warm-green-avocado: #6e8b3d;
|
||||
--warm-green-spring: #a3c85a;
|
||||
}
|
||||
|
||||
|
||||
body {
|
||||
background: url(images/fond.jpg) center center no-repeat fixed #fff;
|
||||
font-family: Quicksand, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 1.42857143;
|
||||
color: #333;
|
||||
border-top: 0;
|
||||
background: oklch(98.5% 0.002 247.839);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-size: cover;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#wrapper {
|
||||
border-top: none;
|
||||
margin-top: 0;
|
||||
padding: 0;
|
||||
background: 0 0;
|
||||
box-shadow: none
|
||||
h1 {
|
||||
margin: auto 0 0;
|
||||
font-size: 26px;
|
||||
}
|
||||
|
||||
input {
|
||||
color: #4a5d5c;
|
||||
}
|
||||
|
||||
#inner {
|
||||
background: transparent;
|
||||
padding-top: 0;
|
||||
width: 350px;
|
||||
max-width: 350px;
|
||||
text-align: center;
|
||||
color:#FFF;
|
||||
}
|
||||
|
||||
#label {
|
||||
text-shadow: none;
|
||||
color: #FFF;
|
||||
font-weight: normal;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#button {
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
text-shadow: none;
|
||||
font-size: 23px;
|
||||
line-height: 1.8;
|
||||
color: #64d29b;
|
||||
background: #586a69;
|
||||
border-radius: 3px;
|
||||
box-shadow: none;
|
||||
height: 53px;
|
||||
border: none;
|
||||
.mission-statement, .pad-datalist {
|
||||
display: block;
|
||||
}
|
||||
|
||||
|
||||
button[type=submit] {
|
||||
border-top-right-radius: 3px;
|
||||
border-bottom-right-radius: 3px;
|
||||
left: 305px;
|
||||
color: #64d29b;
|
||||
background: #586a69;
|
||||
border: none;
|
||||
top: 0;
|
||||
opacity: 1;
|
||||
transition: .2s background;
|
||||
.mission-statement h2 {
|
||||
font-weight: 700;
|
||||
font-size: 2.25rem;
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
padding-top: 4rem;
|
||||
}
|
||||
|
||||
#button:hover,
|
||||
button[type=submit]:hover {
|
||||
cursor: pointer;
|
||||
background: #4a5d5c;
|
||||
color: #64d29b;
|
||||
.mission-statement p {
|
||||
color: var(--muted-text);
|
||||
font-size: 20px;
|
||||
text-align: center;
|
||||
max-width: 40%;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
#wrapper {
|
||||
border-top: 0;
|
||||
margin-top: 0;
|
||||
padding: 0;
|
||||
background: unset;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
#inner {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
margin-top: 20px;
|
||||
margin-bottom: 20px;
|
||||
max-width: 80%;
|
||||
}
|
||||
|
||||
#label {
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.25rem;
|
||||
font-weight: 700;
|
||||
color: rgb(55 65 81);
|
||||
margin-bottom: 0.5rem;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
#go2Name {
|
||||
order: 1;
|
||||
}
|
||||
|
||||
#padname, #go2Name, #go2Name [type="submit"], #button, #button:hover {
|
||||
all: unset;
|
||||
}
|
||||
|
||||
|
||||
#padname {
|
||||
width: 100%;
|
||||
padding: 0.5rem 0.75rem;
|
||||
border: 1px solid #d1d5db;
|
||||
border-radius: 0.375rem;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 0.5rem;
|
||||
outline: none;
|
||||
transition: border 0.2s;
|
||||
}
|
||||
|
||||
#padname {
|
||||
height: 38px;
|
||||
max-width: 350px;
|
||||
padding: 0 12px;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
color: var(--muted-text);
|
||||
border: 1px solid var(--muted-border);
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
#button, #button:hover, #go2Name [type="submit"] {
|
||||
order: 2;
|
||||
margin-top: 0.5rem;
|
||||
line-height: 1.25rem;
|
||||
background: white;
|
||||
border: 1px solid var(--muted-border);
|
||||
text-align: center;
|
||||
padding-top: 0.5rem;
|
||||
padding-bottom: 0.5rem;
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#go2Name [type="submit"]:hover {
|
||||
background-color: oklch(52.7% 0.154 150.069)
|
||||
}
|
||||
|
||||
#button, #button:hover {
|
||||
order: 2;
|
||||
}
|
||||
|
||||
#button:hover {
|
||||
background-color: var(--muted-background);
|
||||
}
|
||||
|
||||
#go2Name input {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
||||
#go2Name [type="submit"] {
|
||||
display: block;
|
||||
background-color: var(--ep-color);
|
||||
color: white;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
||||
body nav {
|
||||
display: flex;
|
||||
border-bottom-width: 1px;
|
||||
border-bottom-style: solid;
|
||||
border-bottom-color: var(--etherpad-border);
|
||||
padding: 1rem 1.5rem;
|
||||
}
|
||||
|
||||
.logo-box svg {
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.logo-box {
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
background: #16a34a;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
|
||||
#wrapper, .pad-datalist {
|
||||
width: 100%;
|
||||
max-width: 28rem;
|
||||
background: #fff;
|
||||
border: 1px solid #e5e7eb;
|
||||
border-radius: 0.75rem;
|
||||
box-shadow: 0 1px 2px 0 #0001;
|
||||
margin: 2rem auto auto;
|
||||
}
|
||||
|
||||
.pad-datalist {
|
||||
max-width: 56rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.break-column {
|
||||
flex-basis: 100%;
|
||||
width: 0;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.recent-pad {
|
||||
padding: 0.75rem 1.5rem;
|
||||
display: flex;
|
||||
position: relative;
|
||||
}
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.body {
|
||||
flex-grow: 1;
|
||||
background: linear-gradient(
|
||||
to bottom right,
|
||||
#d1fae5, /* emerald-100 */
|
||||
#f0fdfa, /* teal-50 */
|
||||
#dbeafe, /* blue-100 */
|
||||
#c7d2fe /* indigo-200 */
|
||||
);
|
||||
}
|
||||
|
||||
.recent-pad:hover a {
|
||||
color: var(--ep-color);
|
||||
}
|
||||
|
||||
.recent-pad-arrow {
|
||||
position: absolute;
|
||||
right: 1rem;
|
||||
}
|
||||
|
||||
.recent-pad a {
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.25rem;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
a, a:visited, a:hover, a:active {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.pad-datalist h2 {
|
||||
border-bottom: 1px solid var(--muted-border);
|
||||
padding: 1rem 1.5rem;
|
||||
border-bottom-width: 1px;
|
||||
border-bottom-style: solid;
|
||||
border-bottom-color: #e5e7eb;
|
||||
}
|
||||
|
||||
.card-content {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
#inner {
|
||||
max-width: 100%;
|
||||
padding: 0 1rem;
|
||||
}
|
||||
|
||||
.mission-statement p {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.pad-datalist {
|
||||
max-width: 90%;
|
||||
}
|
||||
|
||||
.mission-statement h2 {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,137 @@
|
||||
'use strict';
|
||||
|
||||
window.addEventListener('pageshow', (event) => {
|
||||
if (event.persisted) {
|
||||
if (document.readyState === 'complete' || document.readyState === 'interactive') {
|
||||
window.customStart();
|
||||
} else {
|
||||
window.addEventListener('DOMContentLoaded', window.customStart, {once: true});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
window.customStart = () => {
|
||||
document.getElementById('recent-pads').replaceChildren()
|
||||
// define your javascript here
|
||||
// jquery is available - except index.js
|
||||
// you can load extra scripts with $.getScript http://api.jquery.com/jQuery.getScript/
|
||||
const divHoldingPlaceHolderLabel = document
|
||||
.querySelector('[data-l10n-id="index.placeholderPadEnter"]');
|
||||
|
||||
const observer = new MutationObserver(() => {
|
||||
document.querySelector('#go2Name input')
|
||||
.setAttribute('placeholder', divHoldingPlaceHolderLabel.textContent);
|
||||
});
|
||||
|
||||
observer
|
||||
.observe(divHoldingPlaceHolderLabel, {childList: true, subtree: true, characterData: true});
|
||||
|
||||
|
||||
const recentPadList = document.getElementById('recent-pads');
|
||||
const parentStyle = recentPadList.parentElement.style;
|
||||
const recentPadListHeading = document.querySelector('[data-l10n-id="index.recentPads"]');
|
||||
const recentPadsFromLocalStorage = localStorage.getItem('recentPads');
|
||||
let recentPadListData = [];
|
||||
if (recentPadsFromLocalStorage != null) {
|
||||
recentPadListData = JSON.parse(recentPadsFromLocalStorage);
|
||||
}
|
||||
|
||||
// Remove duplicates based on pad name and sort by timestamp
|
||||
recentPadListData = recentPadListData.filter(
|
||||
(pad, index, self) =>
|
||||
index === self.findIndex((p) => p.name === pad.name)
|
||||
).sort((a, b) => new Date(a.timestamp) > new Date(b.timestamp) ? -1 : 1);
|
||||
|
||||
if (recentPadListData.length === 0) {
|
||||
recentPadListHeading.setAttribute('data-l10n-id', 'index.recentPadsEmpty');
|
||||
parentStyle.display = 'flex';
|
||||
parentStyle.justifyContent = 'center';
|
||||
parentStyle.alignItems = 'center';
|
||||
parentStyle.maxHeight = '100%';
|
||||
recentPadList.remove();
|
||||
} else {
|
||||
/**
|
||||
* @typedef {Object} Pad
|
||||
* @property {string} name
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {Pad} pad
|
||||
*/
|
||||
|
||||
const arrowIcon = '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-arrow-right w-4 h-4 text-gray-400"><path d="M5 12h14"></path><path d="m12 5 7 7-7 7"></path></svg>';
|
||||
const clockIcon = '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-clock w-3 h-3"><circle cx="12" cy="12" r="10"></circle><polyline points="12 6 12 12 16 14"></polyline></svg>';
|
||||
const personalIcon = '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-users w-3 h-3"><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"></path><circle cx="9" cy="7" r="4"></circle><path d="M22 21v-2a4 4 0 0 0-3-3.87"></path><path d="M16 3.13a4 4 0 0 1 0 7.75"></path></svg>';
|
||||
recentPadListData.forEach((pad) => {
|
||||
const li = document.createElement('li');
|
||||
|
||||
|
||||
li.style.cursor = 'pointer';
|
||||
|
||||
li.className = 'recent-pad';
|
||||
const padPath = `${window.location.href}p/${pad.name}`;
|
||||
const link = document.createElement('a');
|
||||
link.style.textDecoration = 'none';
|
||||
|
||||
link.href = padPath;
|
||||
link.innerText = pad.name;
|
||||
li.appendChild(link);
|
||||
|
||||
|
||||
const arrowIconElement = document.createElement('span');
|
||||
arrowIconElement.className = 'recent-pad-arrow';
|
||||
arrowIconElement.innerHTML = arrowIcon;
|
||||
li.appendChild(arrowIconElement);
|
||||
|
||||
const nextRow = document.createElement('div');
|
||||
|
||||
nextRow.style.display = 'flex';
|
||||
nextRow.style.gap = '10px';
|
||||
nextRow.style.marginTop = '10px';
|
||||
|
||||
const clockIconElement = document.createElement('span');
|
||||
clockIconElement.className = 'recent-pad-clock';
|
||||
clockIconElement.innerHTML = clockIcon;
|
||||
|
||||
nextRow.appendChild(clockIconElement);
|
||||
|
||||
const time = new Date(pad.timestamp);
|
||||
const userLocale = navigator.language || 'en-US';
|
||||
|
||||
const formattedTime = time.toLocaleDateString(userLocale, {
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
});
|
||||
const timeElement = document.createElement('span');
|
||||
timeElement.className = 'recent-pad-time';
|
||||
timeElement.innerText = formattedTime;
|
||||
|
||||
nextRow.appendChild(timeElement);
|
||||
|
||||
const personalIconElement = document.createElement('span');
|
||||
personalIconElement.className = 'recent-pad-personal';
|
||||
personalIconElement.innerHTML = personalIcon;
|
||||
|
||||
personalIconElement.style.marginLeft = '5px';
|
||||
|
||||
const members = document.createElement('span');
|
||||
members.className = 'recent-pad-members';
|
||||
members.innerText = pad.members;
|
||||
|
||||
|
||||
nextRow.appendChild(personalIconElement);
|
||||
nextRow.appendChild(members);
|
||||
li.appendChild(nextRow);
|
||||
|
||||
li.addEventListener('click', () => {
|
||||
window.location.href = padPath;
|
||||
});
|
||||
|
||||
// https://v0.dev/chat/etherpad-design-clone-qZnwOrVRXxH
|
||||
recentPadList.appendChild(li);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,7 +1,35 @@
|
||||
'use strict';
|
||||
|
||||
const MAX_PADS_IN_HISTORY = 3;
|
||||
|
||||
window.customStart = () => {
|
||||
$('#pad_title').show();
|
||||
$('.buttonicon').on('mousedown', function () { $(this).parent().addClass('pressed'); });
|
||||
$('.buttonicon').on('mouseup', function () { $(this).parent().removeClass('pressed'); });
|
||||
|
||||
const pathSegments = window.location.pathname.split('/');
|
||||
const padName = pathSegments[pathSegments.length - 1];
|
||||
const recentPads = localStorage.getItem('recentPads');
|
||||
if (recentPads == null) {
|
||||
localStorage.setItem('recentPads', JSON.stringify([]));
|
||||
}
|
||||
const recentPadsList = JSON.parse(localStorage.getItem('recentPads'));
|
||||
if (!recentPadsList.some((pad) => pad.name === padName)) {
|
||||
if (recentPadsList.length >= MAX_PADS_IN_HISTORY) {
|
||||
recentPadsList.shift(); // Remove the oldest pad if we have more than 10
|
||||
}
|
||||
recentPadsList.push({
|
||||
name: padName,
|
||||
timestamp: new Date().toISOString(), // Store the timestamp for sorting
|
||||
members: 1,
|
||||
});
|
||||
localStorage.setItem('recentPads', JSON.stringify(recentPadsList));
|
||||
} else {
|
||||
// Update the timestamp if the pad already exists
|
||||
const existingPad = recentPadsList.find((pad) => pad.name === padName);
|
||||
if (existingPad) {
|
||||
existingPad.timestamp = new Date().toISOString();
|
||||
}
|
||||
localStorage.setItem('recentPads', JSON.stringify(recentPadsList));
|
||||
}
|
||||
};
|
||||
|
||||
@ -109,6 +109,10 @@
|
||||
width: 45px;
|
||||
height: 38px;
|
||||
}
|
||||
nav, .mission-statement, .pad-datalist {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media only screen and (min-device-width: 320px) and (max-device-width: 800px) {
|
||||
body {
|
||||
background: #bbb;
|
||||
@ -131,24 +135,45 @@
|
||||
<link href="static/skins/<%=encodeURI(settings.skinName)%>/index.css?v=<%=settings.randomVersionString%>" rel="stylesheet">
|
||||
<% e.end_block(); %>
|
||||
|
||||
<nav>
|
||||
<div class="logo-box">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-file-text w-5 h-5 text-white"><path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"></path><path d="M14 2v4a2 2 0 0 0 2 2h4"></path><path d="M10 9H8"></path><path d="M16 13H8"></path><path d="M16 17H8"></path></svg>
|
||||
</div>
|
||||
<h1>Etherpad</h1>
|
||||
</nav>
|
||||
|
||||
|
||||
<div class="body">
|
||||
<div class="mission-statement">
|
||||
<h2 data-l10n-id="index.createAndShareDocuments"></h2>
|
||||
<p data-l10n-id="index.createAndShareDocumentsDescription"></p>
|
||||
</div>
|
||||
|
||||
<div id="wrapper">
|
||||
<% e.begin_block("indexWrapper"); %>
|
||||
<div id="inner">
|
||||
<% if (!settings.requireSession) { %>
|
||||
<% if (settings.editOnly) { %>
|
||||
<label id="label" for="padname" data-l10n-id="index.openPad"></label>
|
||||
<button data-l10n-id="index.openPad"></button>
|
||||
<% } else {%>
|
||||
<button id="button" data-l10n-id="index.newPad"></button>
|
||||
<label id="label" for="padname" data-l10n-id="index.createOpenPad"></label>
|
||||
<button id="button" data-l10n-id="index.generateNewPad"></button>
|
||||
<% } %>
|
||||
<form action="#" id="go2Name">
|
||||
<input type="text" id="padname" maxlength="50" autofocus x-webkit-speech>
|
||||
<button type="submit">OK</button>
|
||||
<label id="label" for="padname" data-l10n-id="index.labelPad"></label>
|
||||
<input type="text" id="padname" maxlength="50" autofocus placeholder="Enter pad name...">
|
||||
<button type="submit" data-l10n-id="index.createOpenPad"></button>
|
||||
</form>
|
||||
<% } %>
|
||||
</div>
|
||||
<% e.end_block(); %>
|
||||
</div>
|
||||
<div style="display: none" data-l10n-id="index.placeholderPadEnter"></div>
|
||||
<div class="pad-datalist">
|
||||
<h2 data-l10n-id="index.recentPads"></h2>
|
||||
<ul id="recent-pads">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<script src="<%=entrypoint%>"></script>
|
||||
|
||||
<% e.begin_block("indexCustomScripts"); %>
|
||||
|
||||
@ -164,7 +164,7 @@
|
||||
</p>
|
||||
<% e.end_block(); %>
|
||||
</div>
|
||||
<button data-l10n-id="pad.settings.delete" id="delete-pad">Delete pad</button>
|
||||
<button data-l10n-id="pad.settings.deletePad" id="delete-pad">Delete pad</button>
|
||||
<h2 data-l10n-id="pad.settings.about">About</h2>
|
||||
<span data-l10n-id="pad.settings.poweredBy">Powered by</span>
|
||||
<a href="https://etherpad.org" target="_blank" referrerpolicy="no-referrer" rel="noopener">Etherpad</a>
|
||||
|
||||
@ -13,7 +13,6 @@ test.describe('Plugins page', ()=> {
|
||||
await page.waitForSelector('.search-field');
|
||||
const pluginTable = page.locator('table tbody').nth(1);
|
||||
await expect(pluginTable).not.toBeEmpty()
|
||||
await expect(pluginTable.locator('tr')).toHaveCount(190)
|
||||
})
|
||||
|
||||
test('Searches for a plugin', async ({page}) => {
|
||||
@ -33,8 +32,6 @@ test.describe('Plugins page', ()=> {
|
||||
await expect(pluginTable).not.toBeEmpty({
|
||||
timeout: 15000
|
||||
})
|
||||
const plugins = await pluginTable.locator('tr').count()
|
||||
await expect(pluginTable.locator('tr')).toHaveCount(190)
|
||||
|
||||
// Now everything is loaded, lets install a plugin
|
||||
|
||||
|
||||
17
src/tests/frontend-new/specs/editbar.spec.ts
Normal file
17
src/tests/frontend-new/specs/editbar.spec.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import {expect, test} from "@playwright/test";
|
||||
import {clearPadContent, getPadBody, goToNewPad} from "../helper/padHelper";
|
||||
|
||||
test.beforeEach(async ({ page })=>{
|
||||
// create a new pad before each test run
|
||||
await goToNewPad(page);
|
||||
})
|
||||
|
||||
test('should go to home on pad', async ({page}) => {
|
||||
const homeButton = page.locator('.buttonicon.buttonicon-home')
|
||||
const attribute = await homeButton.getAttribute('data-l10n-id')
|
||||
expect(attribute).toBe('pad.toolbar.home.title');
|
||||
|
||||
await homeButton.click();
|
||||
const url = page.url();
|
||||
expect(url).not.toContain('/p/');
|
||||
})
|
||||
@ -38,7 +38,7 @@ test.describe('indentation button', function () {
|
||||
await page.locator('.buttonicon-indent').click()
|
||||
|
||||
// type a bit, make a line break and type again
|
||||
await padBody.locator('div').first().focus()
|
||||
await padBody.focus()
|
||||
await page.keyboard.type('line 1')
|
||||
await page.keyboard.press('Enter');
|
||||
await page.keyboard.type('line 2')
|
||||
|
||||
@ -12,6 +12,6 @@
|
||||
"devDependencies": {
|
||||
"ep_etherpad-lite": "workspace:../src",
|
||||
"typescript": "^5.8.2",
|
||||
"vite": "^6.3.5"
|
||||
"vite": "^7.0.4"
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user