diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml
index a9b8d46d9..9a9dcfebb 100644
--- a/.github/workflows/dependency-review.yml
+++ b/.github/workflows/dependency-review.yml
@@ -17,4 +17,4 @@ jobs:
- name: 'Checkout Repository'
uses: actions/checkout@v4
- name: 'Dependency Review'
- uses: actions/dependency-review-action@v3
+ uses: actions/dependency-review-action@v4
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index e3d61b2d2..dde0d68d5 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -89,3 +89,11 @@ jobs:
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
+ - name: Update repo description
+ uses: peter-evans/dockerhub-description@v4
+ if: github.ref == 'refs/heads/master'
+ with:
+ username: ${{ secrets.DOCKERHUB_USERNAME }}
+ password: ${{ secrets.DOCKERHUB_TOKEN }}
+ repository: etherpad/etherpad
+ enable-url-completion: true
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index e2f0383a2..6c94cbad2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -21,3 +21,4 @@ out/
/src/bin/convertSettings.json
/src/bin/etherpad-1.deb
/src/bin/node.exe
+plugin_packages
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ce90415c3..9a8377712 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,12 @@
+# 1.9.7
+
+### Notable enhancements and fixes
+
+* Added Live Plugin Manager: Plugins are now installed into a separate folder on the host system. This folder is called `plugin_packages`.
+That way the plugins are separated from the normal etherpad installation.
+* Make repairPad.js more verbose
+* Fixed favicon not being loaded correctly
+
# 1.9.6
### Notable enhancements and fixes
@@ -9,7 +18,7 @@
# 1.9.5
-### Compability changes
+### Compatibility changes
* This version deprecates NodeJS16 as it reached its end of life and won't receive any updates. So to get started with Etherpad v1.9.5 you need NodeJS 18 and above.
* The bundled windows NodeJS version has been bumped to the current LTS version 20.
@@ -21,7 +30,7 @@
# 1.9.4
-### Compability changes
+### Compatibility changes
* 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)
diff --git a/doc/api/hooks_server-side.adoc b/doc/api/hooks_server-side.adoc
index 2bc0d3bb6..a5e70ec02 100644
--- a/doc/api/hooks_server-side.adoc
+++ b/doc/api/hooks_server-side.adoc
@@ -1049,8 +1049,7 @@ Context properties:
* `srcFile`: The document to convert.
* `ImportError`: Subclass of Error that can be thrown to provide a specific
error message to the user. The constructor's first argument must be a string
- matching one of the [known error
- identifiers](https://github.com/ether/etherpad-lite/blob/1.8.16/src/static/js/pad_impexp.js==L80-L86).
+ matching one of the https://github.com/ether/etherpad-lite/blob/1.9.6/src/static/js/pad_impexp.js#L80-L86[known error identifiers].
Example:
diff --git a/src/bin/doc/package-lock.json b/src/bin/doc/package-lock.json
index 786aa1e21..a51a03a65 100644
--- a/src/bin/doc/package-lock.json
+++ b/src/bin/doc/package-lock.json
@@ -5,9 +5,9 @@
"requires": true,
"dependencies": {
"marked": {
- "version": "11.1.0",
- "resolved": "https://registry.npmjs.org/marked/-/marked-11.1.0.tgz",
- "integrity": "sha512-fvKJWAPEafVj1dwGwcPI5mBB/0pvViL6NlCbNDG1HOIRwwAU/jeMoFxfbRLuirO1wRH7m4yPvBqD/O1wyWvayw=="
+ "version": "11.2.0",
+ "resolved": "https://registry.npmjs.org/marked/-/marked-11.2.0.tgz",
+ "integrity": "sha512-HR0m3bvu0jAPYiIvLUUQtdg1g6D247//lvcekpHO1WMvbwDlwSkZAX9Lw4F4YHE1T0HaaNve0tuAWuV1UJ6vtw=="
}
}
}
diff --git a/src/bin/doc/package.json b/src/bin/doc/package.json
index 1ca0fa87c..e2e77b8c1 100644
--- a/src/bin/doc/package.json
+++ b/src/bin/doc/package.json
@@ -7,7 +7,7 @@
"node": ">=12.17.0"
},
"dependencies": {
- "marked": "^11.1.0"
+ "marked": "^11.2.0"
},
"devDependencies": {},
"optionalDependencies": {},
diff --git a/src/bin/push-after-release.sh b/src/bin/push-after-release.sh
new file mode 100644
index 000000000..8781196fb
--- /dev/null
+++ b/src/bin/push-after-release.sh
@@ -0,0 +1,16 @@
+#!/bin/bash
+
+# Specify the path to your package.json file
+PACKAGE_JSON_PATH="./src//package.json"
+
+# Check if the file exists
+if [ ! -f "$PACKAGE_JSON_PATH" ]; then
+ echo "Error: package.json not found in the specified path."
+ exit 1
+fi
+
+# Read the version from package.json into a variable
+VERSION=$(jq -r '.version' "$PACKAGE_JSON_PATH")
+git push origin master develop $VERSION
+git push --tags
+(cd ../ether.github.com && git push)
\ No newline at end of file
diff --git a/src/bin/release.js b/src/bin/release.js
index d6cc3a97b..ee5f245e7 100644
--- a/src/bin/release.js
+++ b/src/bin/release.js
@@ -207,11 +207,10 @@ console.log(' git log --graph --date-order --boundary --oneline --decorate deve
console.log(` git show '${newVersion}'`);
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('Run ./bin/push-after-release.sh');
console.log('Creating a Windows build is not necessary anymore and will be created by GitHub action');
-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('After the windows binary is created a new release with the set version is created automatically.' +
+ ' Just paste the release notes in there');
console.log('The docs are updated automatically with the new version. While the windows build' +
' is generated people can still download the older versions.');
console.log('Finally go public with an announcement via our comms channels :)');
diff --git a/src/bin/repairPad.js b/src/bin/repairPad.js
index 7983fc88d..ed1d83659 100644
--- a/src/bin/repairPad.js
+++ b/src/bin/repairPad.js
@@ -43,7 +43,9 @@ let valueCount = 0;
neededDBValues.push(`pad:${padId}:chat:${chat}`);
}
// now fetch and reinsert every key
+ console.log('Fetch and reinsert every key');
for (const key of neededDBValues) {
+ if (valueCount % 100 === 0) console.log(valueCount + "/" + neededDBValues.length);
const value = await db.get(key);
// if it isn't a globalAuthor value which we want to ignore..
// console.log(`Key: ${key}, value: ${JSON.stringify(value)}`);
diff --git a/src/locales/az.json b/src/locales/az.json
index 6daaa4719..2bd1245e9 100644
--- a/src/locales/az.json
+++ b/src/locales/az.json
@@ -15,9 +15,9 @@
},
"index.newPad": "Yeni lövhə",
"index.createOpenPad": "və ya lövhəni bu adla yarat/aç:",
- "pad.toolbar.bold.title": "Qalın (Ctrl-B)",
- "pad.toolbar.italic.title": "Kursiv (Ctrl-I)",
- "pad.toolbar.underline.title": "Altından xətt çəkmə (Ctrl-U)",
+ "pad.toolbar.bold.title": "Qalın (Ctrl+B)",
+ "pad.toolbar.italic.title": "Kursiv (Ctrl+I)",
+ "pad.toolbar.underline.title": "Altından xətt çəkmə (Ctrl+U)",
"pad.toolbar.strikethrough.title": "Üstdən xətləmək (Ctrl+5)",
"pad.toolbar.ol.title": "Sıralanmış siyahı (Ctrl+Shift+N)",
"pad.toolbar.ul.title": "Sırasız siyahı (Ctrl+Shift+L)",
diff --git a/src/locales/bn.json b/src/locales/bn.json
index aa551f807..ac422714b 100644
--- a/src/locales/bn.json
+++ b/src/locales/bn.json
@@ -3,6 +3,7 @@
"authors": [
"Aftab1995",
"Aftabuzzaman",
+ "Aishik Rehman",
"Al Riaz Uddin Ripon",
"Bellayet",
"Greatder",
@@ -84,7 +85,7 @@
"pad.settings.about": "পরিচিতি",
"pad.settings.poweredBy": "এটি দ্বারা চালিত:",
"pad.importExport.import_export": "আমদানি/রপ্তানি",
- "pad.importExport.import": "কোন টেক্সট ফাইল বা নথি আপলোড করুন",
+ "pad.importExport.import": "কোনো টেক্সট ফাইল বা নথি আপলোড করুন",
"pad.importExport.importSuccessful": "সফল!",
"pad.importExport.export": "এইরূপে এই প্যাডটি রপ্তানি করুন:",
"pad.importExport.exportetherpad": "ইথারপ্যাড",
@@ -150,7 +151,7 @@
"timeslider.unnamedauthors": "নামবিহীন {{num}} জন {[plural(num) one: লেখক, other: লেখক ]}",
"pad.savedrevs.marked": "এই সংশোধনটি এখন সংরক্ষিত সংশোধন হিসেবে চিহ্নিত করা হয়েছে",
"pad.userlist.entername": "আপনার নাম লিখুন",
- "pad.userlist.unnamed": "কোন নাম নির্বাচন করা হয়নি",
+ "pad.userlist.unnamed": "কোনো নাম নির্বাচন করা হয়নি",
"pad.impexp.importbutton": "এখন আমদানি করুন",
"pad.impexp.importing": "আমদানি হচ্ছে...",
"pad.impexp.padHasData": "আমরা এই ফাইলটি আমদানি করতে সক্ষম হয়নি কারণ এই প্যাড ইতিমধ্যে পরিবর্তিত হয়েছে, দয়া করে একটি নতুন প্যাডে অামদানি করুন।",
diff --git a/src/locales/de.json b/src/locales/de.json
index 4204bc37c..92e203ef2 100644
--- a/src/locales/de.json
+++ b/src/locales/de.json
@@ -77,8 +77,8 @@
"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",
- "pad.settings.chatandusers": "Unterhaltung und Benutzer anzeigen",
+ "pad.settings.stickychat": "Chat immer anzeigen",
+ "pad.settings.chatandusers": "Chat und Benutzer anzeigen",
"pad.settings.colorcheck": "Autorenfarben anzeigen",
"pad.settings.linenocheck": "Zeilennummern",
"pad.settings.rtlcheck": "Inhalt von rechts nach links lesen?",
@@ -132,7 +132,7 @@
"pad.share.readonly": "Eingeschränkter Nur-Lese-Zugriff",
"pad.share.link": "Verknüpfung",
"pad.share.emebdcode": "In Webseite einbetten",
- "pad.chat": "Unterhaltung",
+ "pad.chat": "Chat",
"pad.chat.title": "Den Chat für dieses Pad öffnen.",
"pad.chat.loadmessages": "Weitere Nachrichten laden",
"pad.chat.stick.title": "Chat an den Bildschirm anheften",
diff --git a/src/locales/lb.json b/src/locales/lb.json
index 0c5370021..8bb3beb77 100644
--- a/src/locales/lb.json
+++ b/src/locales/lb.json
@@ -64,6 +64,7 @@
"pad.modals.unauth.explanation": "Är Rechter hu geännert während deem Dir dës säit gekuckt hutt. Probéiert fir Iech nei ze connectéieren.",
"pad.modals.looping.explanation": "Et gëtt Kommunikatiounsproblemer mam Synchronisatiouns-Server.",
"pad.modals.initsocketfail": "De Server kann net erreecht ginn.",
+ "pad.modals.initsocketfail.explanation": "Et konnt keng Verbindung mam Synchronisatiounsserver opgeholl ginn.",
"pad.modals.slowcommit.explanation": "De Server äntwert net.",
"pad.modals.deleted": "Geläscht.",
"pad.modals.deleted.explanation": "Dëse Pad gouf geläscht.",
diff --git a/src/locales/pl.json b/src/locales/pl.json
index d5d85cede..6f01cd6d2 100644
--- a/src/locales/pl.json
+++ b/src/locales/pl.json
@@ -2,6 +2,7 @@
"@metadata": {
"authors": [
"DeRudySoulStorm",
+ "Iketsi",
"Kareyac",
"Macofe",
"Mateon1",
@@ -15,8 +16,19 @@
"Woytecr"
]
},
+ "admin.page-title": "Panel administracyjny – Etherpad",
+ "admin_plugins": "Menedżer wtyczek",
+ "admin_plugins.available": "Dostępne wtyczki",
+ "admin_plugins.available_not-found": "Nie znaleziono żadnych wtyczek.",
+ "admin_plugins.available_install.value": "Instaluj",
"admin_plugins.description": "Opis",
+ "admin_plugins.installed": "Zainstalowane wtyczki",
+ "admin_plugins.installed_uninstall.value": "Odinstaluj",
+ "admin_plugins.last-update": "Ostatnia aktualizacja",
+ "admin_plugins.name": "Nazwa",
+ "admin_plugins.page-title": "Menedżer wtyczek - Etherpad",
"admin_plugins.version": "Wersja",
+ "admin_plugins_info.version": "Wersja Etherpada",
"admin_settings": "Ustawienia",
"index.newPad": "Nowy dokument",
"index.createOpenPad": "lub stwórz/otwórz dokument o nazwie:",
diff --git a/src/locales/ru.json b/src/locales/ru.json
index 792162e57..f8e7fb340 100644
--- a/src/locales/ru.json
+++ b/src/locales/ru.json
@@ -12,6 +12,7 @@
"Movses",
"Nzeemin",
"Okras",
+ "Pacha Tchernof",
"Patrick Star",
"Teretalexev",
"Volkov",
@@ -73,7 +74,7 @@
"pad.toolbar.showusers.title": "Показать пользователей в документе",
"pad.colorpicker.save": "Сохранить",
"pad.colorpicker.cancel": "Отмена",
- "pad.loading": "Загрузка...",
+ "pad.loading": "Загружается…",
"pad.noCookie": "Куки не найдены. Пожалуйста, включите куки в вашем браузере! Ваш сеанс и настройки не будут сохранены между посещениями. Это может быть связано с тем, что Etherpad включен в iFrame в некоторых браузерах. Убедитесь, что Etherpad находится в том же поддомене/домене, что и родительский iFrame.",
"pad.permissionDenied": "У вас нет разрешения на доступ",
"pad.settings.padSettings": "Настройки документа",
diff --git a/src/locales/zh-hans.json b/src/locales/zh-hans.json
index 69a908ee4..5a8858424 100644
--- a/src/locales/zh-hans.json
+++ b/src/locales/zh-hans.json
@@ -5,6 +5,7 @@
"Cosing",
"Dimension",
"GuoPC",
+ "HellojoeAoPS",
"Hydra",
"Hzy980512",
"JuneAugust",
@@ -28,20 +29,20 @@
"admin_plugins.available_not-found": "找不到插件。",
"admin_plugins.available_fetching": "获取中…",
"admin_plugins.available_install.value": "安装",
- "admin_plugins.available_search.placeholder": "搜索插件以安装",
+ "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.last-update": "最近更新",
"admin_plugins.name": "名称",
"admin_plugins.page-title": "插件管理器 - Etherpad",
"admin_plugins.version": "版本",
"admin_plugins_info": "故障排除信息",
- "admin_plugins_info.hooks": "已安装的钩子",
- "admin_plugins_info.hooks_client": "客户端钩子",
- "admin_plugins_info.hooks_server": "服务器端钩子",
+ "admin_plugins_info.hooks": "已安装的挂钩",
+ "admin_plugins_info.hooks_client": "客户端挂钩",
+ "admin_plugins_info.hooks_server": "服务器端挂钩",
"admin_plugins_info.parts": "已安装部分",
"admin_plugins_info.plugins": "已安装插件",
"admin_plugins_info.page-title": "插件信息 - Etherpad",
@@ -56,7 +57,7 @@
"admin_settings.current_save.value": "保存设置",
"admin_settings.page-title": "设置 - Etherpad",
"index.newPad": "新记事本",
- "index.createOpenPad": "或者创建/打开带名字的记事本:",
+ "index.createOpenPad": "或创建/打开一个名为以下的记事本:",
"index.openPad": "打开一个现有的记事本,名称为:",
"pad.toolbar.bold.title": "粗体(Ctrl-B)",
"pad.toolbar.italic.title": "斜体(Ctrl-I)",
@@ -64,7 +65,7 @@
"pad.toolbar.strikethrough.title": "删除线(Ctrl+5)",
"pad.toolbar.ol.title": "有序列表(Ctrl+Shift+N)",
"pad.toolbar.ul.title": "无序列表(Ctrl+Shift+L)",
- "pad.toolbar.indent.title": "增加缩进(TAB)",
+ "pad.toolbar.indent.title": "缩进(TAB)",
"pad.toolbar.unindent.title": "减少缩进(Shift+TAB)",
"pad.toolbar.undo.title": "撤消(Ctrl-Z)",
"pad.toolbar.redo.title": "重做(Ctrl+Y)",
@@ -78,7 +79,7 @@
"pad.colorpicker.save": "保存",
"pad.colorpicker.cancel": "取消",
"pad.loading": "加载中...",
- "pad.noCookie": "无法找到Cookie。请在您的浏览器中允许Cookie!您的会话和设置不会在访问时保存。这可能是由于Etherpad包含在某些浏览器中的iFrame中。请确保Etherpad与父iFrame位于同一子域/域上",
+ "pad.noCookie": "无法找到 Cookie。请在您的浏览器中允许cookie!您的会话和设置不会在两次访问之间保存。这可能是由于 Etherpad 包含在某些浏览器的 iFrame 中。请确保 Etherpad 与父 iFrame 位于同一子域/域中",
"pad.permissionDenied": "您没有访问这个记事本的权限",
"pad.settings.padSettings": "记事本设置",
"pad.settings.myView": "我的视窗",
@@ -110,7 +111,7 @@
"pad.modals.cancel": "取消",
"pad.modals.userdup": "在另一个窗口中打开",
"pad.modals.userdup.explanation": "此记事本似乎在本电脑上的多个浏览器窗口中打开。",
- "pad.modals.userdup.advice": "重新连接,使用此窗口。",
+ "pad.modals.userdup.advice": "重新连接以使用此窗口替代。",
"pad.modals.unauth": "未授权",
"pad.modals.unauth.explanation": "您的权限在查看此页面时已改变。尝试重新连接。",
"pad.modals.looping.explanation": "与同步服务器的通信出现问题。",
@@ -121,17 +122,17 @@
"pad.modals.slowcommit.explanation": "服务器没有响应。",
"pad.modals.slowcommit.cause": "这可能是由于网络连接问题。",
"pad.modals.badChangeset.explanation": "您的一个编辑被同步服务器分类为非法。",
- "pad.modals.badChangeset.cause": "这可能是因为服务器配置的错误或者其他未预料到的行为。如果您认为这是错误,请联系服务管理员。要继续编辑,请尝试重新连接。",
+ "pad.modals.badChangeset.cause": "这可能是由于服务器配置错误或其他一些意外行为造成的。如果您认为这是一个错误,请联系服务管理员。尝试重新连接以继续编辑。",
"pad.modals.corruptPad.explanation": "您试图连接的记事本已损坏。",
- "pad.modals.corruptPad.cause": "这可能是因为服务器配置的错误或者其他未预料到的行为。请联系服务管理员。",
+ "pad.modals.corruptPad.cause": "这可能是由于服务器配置错误或其他一些意外行为造成的。请联系服务管理员。",
"pad.modals.deleted": "已删除。",
"pad.modals.deleted.explanation": "此记事本已被移除。",
- "pad.modals.rateLimited": "速率限制",
- "pad.modals.rateLimited.explanation": "您向此记事本发送了太多消息,因此中断了与您的连接。",
+ "pad.modals.rateLimited": "费率有限。",
+ "pad.modals.rateLimited.explanation": "您向此平板发送了太多消息,因此它断开了您的连接。",
"pad.modals.rejected.explanation": "服务器拒绝了您的浏览器发送的信息。",
- "pad.modals.rejected.cause": "服务器可能在你查看页面时更新了,也可能是Etherpad出现了错误。请尝试重新加载页面。",
+ "pad.modals.rejected.cause": "服务器可能在你查看记事本时更新了,也可能是Etherpad出现了错误。请尝试重新加载页面。",
"pad.modals.disconnected": "您已断开连接。",
- "pad.modals.disconnected.explanation": "到服务器的连接已丢失",
+ "pad.modals.disconnected.explanation": "与服务器的连接丢失",
"pad.modals.disconnected.cause": "服务器可能无法使用。若此情况持续发生,请通知服务器管理员。",
"pad.share": "分享此记事本",
"pad.share.readonly": "只读",
@@ -141,8 +142,8 @@
"pad.chat.title": "打开此记事本的聊天窗口。",
"pad.chat.loadmessages": "加载更多信息",
"pad.chat.stick.title": "在屏幕上固定聊天界面",
- "pad.chat.writeMessage.placeholder": "在此写下您的消息",
- "timeslider.followContents": "跟随记事本的内容更新",
+ "pad.chat.writeMessage.placeholder": "在这里写下您的留言",
+ "timeslider.followContents": "关注记事本内容更新",
"timeslider.pageTitle": "{{appTitle}} 时间轴",
"timeslider.toolbar.returnbutton": "返回记事本",
"timeslider.toolbar.authors": "作者:",
@@ -150,11 +151,11 @@
"timeslider.toolbar.exportlink.title": "导出",
"timeslider.exportCurrent": "当前版本导出为:",
"timeslider.version": "版本 {{version}}",
- "timeslider.saved": "在{{year}}年{{month}}{{day}}日保存",
- "timeslider.playPause": "回放 / 暂停Pad内容",
- "timeslider.backRevision": "返回此Pad的一次修订",
- "timeslider.forwardRevision": "前往此Pad的一次修订",
- "timeslider.dateformat": "{{year}}年{{month}}月{{day}}日 {{hours}}:{{minutes}}:{{seconds}}",
+ "timeslider.saved": "在{{月}}{{日}}{{年}}保存",
+ "timeslider.playPause": "回放 / 暂停记事本内容",
+ "timeslider.backRevision": "返回此记事本的一次修订",
+ "timeslider.forwardRevision": "前往此记事本的下一次修订",
+ "timeslider.dateformat": "{{月}}/{{日}}/{{年}} {{小时}}:{{分钟}}:{{秒}}",
"timeslider.month.january": "1月",
"timeslider.month.february": "2月",
"timeslider.month.march": "3月",
@@ -167,13 +168,13 @@
"timeslider.month.october": "10月",
"timeslider.month.november": "11月",
"timeslider.month.december": "12月",
- "timeslider.unnamedauthors": "{{num}}个匿名作者",
+ "timeslider.unnamedauthors": "{{num}} 未命名 {[plural(num) one: author, other: authors ]}",
"pad.savedrevs.marked": "这一修订现在被标记为已保存的修订版本",
"pad.savedrevs.timeslider": "您可以使用时间轴查阅已保存的版本",
"pad.userlist.entername": "输入您的姓名",
- "pad.userlist.unnamed": "匿名",
- "pad.editbar.clearcolors": "清除整个文档的作者颜色吗?此操作无法撤消",
- "pad.impexp.importbutton": "现在导入",
+ "pad.userlist.unnamed": "未命名的",
+ "pad.editbar.clearcolors": "清除整个文档的作者颜色?这不能被撤消",
+ "pad.impexp.importbutton": "立即导入",
"pad.impexp.importing": "正在导入...",
"pad.impexp.confirmimport": "导入的文件将覆盖记事本的当前文本。你确定要继续吗?",
"pad.impexp.convertFailed": "我们无法导入此文档。请使用其他文档格式或手动复制贴上。",
diff --git a/src/locales/zh-hant.json b/src/locales/zh-hant.json
index 581f6ff7b..434145efa 100644
--- a/src/locales/zh-hant.json
+++ b/src/locales/zh-hant.json
@@ -1,6 +1,7 @@
{
"@metadata": {
"authors": [
+ "HellojoeAoPS",
"Justincheng12345",
"Kly",
"LNDDYL",
@@ -14,8 +15,8 @@
]
},
"admin.page-title": "管理員面板 - Etherpad",
- "admin_plugins": "套件管理",
- "admin_plugins.available": "可用的套件",
+ "admin_plugins": "外掛程式管理器",
+ "admin_plugins.available": "可用套件",
"admin_plugins.available_not-found": "沒有找到套件。",
"admin_plugins.available_fetching": "正在取得…",
"admin_plugins.available_install.value": "安裝",
@@ -38,11 +39,11 @@
"admin_plugins_info.page-title": "套件資訊 - Etherpad",
"admin_plugins_info.version": "Etherpad 版本",
"admin_plugins_info.version_latest": "最新可用版本",
- "admin_plugins_info.version_number": "版本號碼",
+ "admin_plugins_info.version_number": "版本號",
"admin_settings": "設定",
- "admin_settings.current": "目前設置",
+ "admin_settings.current": "目前配置",
"admin_settings.current_example-devel": "範例開發設定模板",
- "admin_settings.current_example-prod": "範例生產設定模板",
+ "admin_settings.current_example-prod": "生產設定模板範例",
"admin_settings.current_restart.value": "重新啟動 Etherpad",
"admin_settings.current_save.value": "儲存設定",
"admin_settings.page-title": "設定 - Etherpad",
@@ -56,11 +57,11 @@
"pad.toolbar.ol.title": "有序清單(Ctrl+Shift+N)",
"pad.toolbar.ul.title": "無序清單(Ctrl+Shift+L)",
"pad.toolbar.indent.title": "縮排(TAB)",
- "pad.toolbar.unindent.title": "縮排(Shift+TAB)",
+ "pad.toolbar.unindent.title": "減少縮排(Shift+TAB)",
"pad.toolbar.undo.title": "復原(Ctrl+Z)",
"pad.toolbar.redo.title": "重做 (Ctrl+Y)",
- "pad.toolbar.clearAuthorship.title": "清除協作者顏色區別 (Ctrl+Shift+C)",
- "pad.toolbar.import_export.title": "以其他檔案格式匯入/匯出",
+ "pad.toolbar.clearAuthorship.title": "清除作者顏色 (Ctrl+Shift+C)",
+ "pad.toolbar.import_export.title": "從不同的檔案格式匯入/匯出",
"pad.toolbar.timeslider.title": "時間軸",
"pad.toolbar.savedRevision.title": "儲存修訂版",
"pad.toolbar.settings.title": "設定",
@@ -69,22 +70,22 @@
"pad.colorpicker.save": "儲存",
"pad.colorpicker.cancel": "取消",
"pad.loading": "載入中...",
- "pad.noCookie": "找不到 Cookie。請允許瀏覽器使用 Cookie!您的 session 與設定沒有在訪問期間被儲存下來,這可能是因為在某些瀏覽器裡 Etherpad 被包在 iFrame 中,請確認 Etherpad 是在父層級的 iFrame 相同的網域/子網域。",
+ "pad.noCookie": "無法找到 Cookie。請在您的瀏覽器中允許cookie!您的 session 和設定未在訪問期間保存下來。這可能是由於 Etherpad 包含在某些瀏覽器的 iFrame 中。請確保 Etherpad 與父層級 iFrame 位於同一子網域/網域中",
"pad.permissionDenied": "你沒有存取這個記事本的權限",
"pad.settings.padSettings": "記事本設定",
"pad.settings.myView": "我的視窗",
"pad.settings.stickychat": "永遠在螢幕上顯示聊天",
- "pad.settings.chatandusers": "顯示聊天與使用者",
- "pad.settings.colorcheck": "協作者顏色",
+ "pad.settings.chatandusers": "顯示聊天和使用者",
+ "pad.settings.colorcheck": "作者顏色",
"pad.settings.linenocheck": "行號",
"pad.settings.rtlcheck": "從右至左讀取內容?",
"pad.settings.fontType": "字型類型:",
"pad.settings.fontType.normal": "正常",
"pad.settings.language": "語言:",
"pad.settings.about": "關於",
- "pad.settings.poweredBy": "技術提供來自",
- "pad.importExport.import_export": "匯入/匯出",
- "pad.importExport.import": "上載任何文字檔或文件",
+ "pad.settings.poweredBy": "技術支援來自",
+ "pad.importExport.import_export": "導入/匯出",
+ "pad.importExport.import": "上傳任何文字檔案或文件",
"pad.importExport.importSuccessful": "完成!",
"pad.importExport.export": "匯出目前的記事本為:",
"pad.importExport.exportetherpad": "Etherpad",
@@ -93,17 +94,17 @@
"pad.importExport.exportword": "Microsoft Word",
"pad.importExport.exportpdf": "PDF",
"pad.importExport.exportopen": "ODF(開放文件格式)",
- "pad.importExport.abiword.innerHTML": "您只可以從純文字或 HTML 格式檔匯入。安裝\n AbiWord 或是 LibreOffice 以取得更多進階的匯入功能。",
+ "pad.importExport.abiword.innerHTML": "您只可以匯入純文字或HTML格式。若要取得更進階的導入功能,請安裝AbiWord 或是LibreOffice。",
"pad.modals.connected": "已連線。",
"pad.modals.reconnecting": "重新連線到您的記事本…",
"pad.modals.forcereconnect": "強制重新連線",
- "pad.modals.reconnecttimer": "下一次重新連線會在",
+ "pad.modals.reconnecttimer": "嘗試重新連接",
"pad.modals.cancel": "取消",
"pad.modals.userdup": "在另一個視窗中開啟",
"pad.modals.userdup.explanation": "此記事本似乎在此電腦上的多個瀏覽器視窗中開啟。",
- "pad.modals.userdup.advice": "重新連線,並改用這個視窗。",
+ "pad.modals.userdup.advice": "重新連線以使用此視窗替代。",
"pad.modals.unauth": "未授權",
- "pad.modals.unauth.explanation": "您的權限在變更此頁時變更了。請嘗試重新連線。",
+ "pad.modals.unauth.explanation": "您的權限在查看此頁面時已改變。嘗試重新連線。",
"pad.modals.looping.explanation": "與同步伺服器間有通訊問題。",
"pad.modals.looping.cause": "也許您是透過不相容的防火牆或代理伺服器連線。",
"pad.modals.initsocketfail": "無法存取伺服器。",
@@ -111,18 +112,18 @@
"pad.modals.initsocketfail.cause": "這可能是因為瀏覽器或網際網路連線問題所造成。",
"pad.modals.slowcommit.explanation": "伺服器沒有回應。",
"pad.modals.slowcommit.cause": "這可能是因為網路連線問題所造成。",
- "pad.modals.badChangeset.explanation": "您的一個編輯被同步伺服器類為非法。",
- "pad.modals.badChangeset.cause": "這可能由於伺服器的配置錯誤或遇到意外問題。若您認為這是錯誤,請聯繫伺服器管理員。如要繼續編輯,請嘗試重新連接。",
+ "pad.modals.badChangeset.explanation": "您所做的一個編輯被同步伺服器歸類為非法。",
+ "pad.modals.badChangeset.cause": "這可能是由於伺服器配置錯誤或其他一些意外行為造成的。如果您認為這是一個錯誤,請聯絡服務管理員。嘗試重新連線以繼續編輯。",
"pad.modals.corruptPad.explanation": "您試圖存取的記事本已損壞。",
- "pad.modals.corruptPad.cause": "這可能由於伺服器的配置錯誤或遇到意外問題。請聯繫伺服器管理員。",
+ "pad.modals.corruptPad.cause": "這可能是由於伺服器配置錯誤或其他一些意外行為造成的。請聯絡服務管理員。",
"pad.modals.deleted": "已刪除。",
"pad.modals.deleted.explanation": "此記事本已被移除。",
- "pad.modals.rateLimited": "比例限制。",
+ "pad.modals.rateLimited": "速率限制。",
"pad.modals.rateLimited.explanation": "您發送太多訊息到此記事本,因此中斷了您的連結。",
"pad.modals.rejected.explanation": "伺服器拒絕了由您的瀏覽器發送的訊息。",
- "pad.modals.rejected.cause": "當您在檢視記事本時伺服器可能正在更新,或是在 Etherpad 裡有臭蟲。請嘗試重新載入頁面。",
+ "pad.modals.rejected.cause": "伺服器可能在你查看記事本時更新了,也可能是Etherpad出現了錯誤。請嘗試重新載入頁面。",
"pad.modals.disconnected": "您已中斷連線。",
- "pad.modals.disconnected.explanation": "伺服器連接曾中斷",
+ "pad.modals.disconnected.explanation": "與伺服器的連線遺失",
"pad.modals.disconnected.cause": "伺服器可能無法使用。若此情況持續發生,請通知伺服器管理員。",
"pad.share": "分享此記事本",
"pad.share.readonly": "唯讀",
@@ -131,20 +132,20 @@
"pad.chat": "聊天功能",
"pad.chat.title": "打開記事本聊天功能",
"pad.chat.loadmessages": "載入更多訊息",
- "pad.chat.stick.title": "釘住聊天在螢幕上",
- "pad.chat.writeMessage.placeholder": "在此編寫您的訊息",
+ "pad.chat.stick.title": "在螢幕上固定聊天介面",
+ "pad.chat.writeMessage.placeholder": "在這裡寫下您的留言",
"timeslider.followContents": "關注記事本內容更新",
"timeslider.pageTitle": "{{appTitle}}時間軸",
"timeslider.toolbar.returnbutton": "返回記事本",
- "timeslider.toolbar.authors": "協作者:",
- "timeslider.toolbar.authorsList": "無協作者",
+ "timeslider.toolbar.authors": "作者:",
+ "timeslider.toolbar.authorsList": "無作者",
"timeslider.toolbar.exportlink.title": "匯出",
"timeslider.exportCurrent": "匯出當前版本為:",
"timeslider.version": "版本{{version}}",
- "timeslider.saved": "{{year}}年{{month}}{{day}}日儲存",
- "timeslider.playPause": "放送 / 暫停記事本內容",
+ "timeslider.saved": "儲存於 {{year}} {{month}} {{day}}",
+ "timeslider.playPause": "重播 / 暫停記事本內容",
"timeslider.backRevision": "返回此記事本的前一次修訂",
- "timeslider.forwardRevision": "前往此記事本的前一次修訂",
+ "timeslider.forwardRevision": "前往此記事本的下一次修訂",
"timeslider.dateformat": "{{year}}年{{month}}月{{day}}日 {{hours}}:{{minutes}}:{{seconds}}",
"timeslider.month.january": "1月",
"timeslider.month.february": "2月",
@@ -158,20 +159,20 @@
"timeslider.month.october": "10月",
"timeslider.month.november": "11月",
"timeslider.month.december": "12月",
- "timeslider.unnamedauthors": "{{num}} 個匿名{[plural(num) one:作者, other:作者]}",
+ "timeslider.unnamedauthors": "{{num}} 個匿名{[plural(num) one: author, other: authors]}",
"pad.savedrevs.marked": "標記此修訂版本為已儲存修訂版本。",
- "pad.savedrevs.timeslider": "您可使用時段滑標來查看先前保存的版本內容",
+ "pad.savedrevs.timeslider": "您可以透過造訪時間滑桿查看已儲存的修訂",
"pad.userlist.entername": "輸入您的姓名",
"pad.userlist.unnamed": "未命名",
- "pad.editbar.clearcolors": "清除整個文檔的協作者顏色區別嗎?此操作無法還原",
+ "pad.editbar.clearcolors": "清除整個文件的作者顏色?這不能被撤銷",
"pad.impexp.importbutton": "現在匯入",
"pad.impexp.importing": "匯入中...",
"pad.impexp.confirmimport": "匯入的檔案將會覆蓋記事本內目前的文字。您確定要繼續嗎?",
"pad.impexp.convertFailed": "未能匯入此檔案。請以其他檔案格式或手動複製貼上匯入。",
- "pad.impexp.padHasData": "此記事本已異動過所以無法匯入該檔案,請匯入至另一個記事本試試。",
+ "pad.impexp.padHasData": "因為此記事本已異動過我們無法匯入此檔案,請匯入至新的記事本",
"pad.impexp.uploadFailed": "上載失敗,請重試",
"pad.impexp.importfailed": "匯入失敗",
"pad.impexp.copypaste": "請複製貼上",
"pad.impexp.exportdisabled": "{{type}}格式的匯出被禁用。有關詳情,請與您的系統管理員聯繫。",
- "pad.impexp.maxFileSize": "檔案太大。請聯絡您的網站管理員來增加用於匯入的允許檔案大小。"
+ "pad.impexp.maxFileSize": "檔案太大。請聯絡您的網站管理員以增加允許匯入的檔案大小"
}
diff --git a/src/node/eejs/index.js b/src/node/eejs/index.js
index 1bf29634b..31ff2c1a5 100644
--- a/src/node/eejs/index.js
+++ b/src/node/eejs/index.js
@@ -76,6 +76,7 @@ exports.require = (name, args, mod) => {
basedir = path.dirname(mod.filename);
paths = mod.paths;
}
+ paths.push(settings.root + '/plugin_packages')
const ejspath = resolve.sync(name, {paths, basedir, extensions: ['.html', '.ejs']});
diff --git a/src/node/handler/ImportHandler.js b/src/node/handler/ImportHandler.js
index 6caf7ba93..35ceddfdd 100644
--- a/src/node/handler/ImportHandler.js
+++ b/src/node/handler/ImportHandler.js
@@ -114,7 +114,7 @@ const doImport = async (req, res, padId, authorId) => {
// 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
- const fileEnding = path.extname(srcFile).toLowerCase();
+ const fileEnding = path.extname(files.file[0].originalFilename).toLowerCase();
const knownFileEndings =
['.txt', '.doc', '.docx', '.pdf', '.odt', '.html', '.htm', '.etherpad', '.rtf'];
const fileEndingUnknown = (knownFileEndings.indexOf(fileEnding) < 0);
diff --git a/src/node/hooks/express/specialpages.js b/src/node/hooks/express/specialpages.js
index 4f41d8cd0..18a17988a 100644
--- a/src/node/hooks/express/specialpages.js
+++ b/src/node/hooks/express/specialpages.js
@@ -43,6 +43,16 @@ exports.expressPreSession = async (hookName, {app}) => {
app.get('/favicon.ico', (req, res, next) => {
(async () => {
+ /*
+ If this is a url we simply redirect to that one.
+ */
+ if (settings.favicon && settings.favicon.startsWith('http')) {
+ res.redirect(settings.favicon);
+ res.send();
+ return;
+ }
+
+
const fns = [
...(settings.favicon ? [path.resolve(settings.root, settings.favicon)] : []),
path.join(settings.root, 'src', 'static', 'skins', settings.skinName, 'favicon.ico'),
diff --git a/src/node/hooks/i18n.js b/src/node/hooks/i18n.js
index c54348867..fc9f00c72 100644
--- a/src/node/hooks/i18n.js
+++ b/src/node/hooks/i18n.js
@@ -74,6 +74,24 @@ const getAllLocales = () => {
if (typeof overrides !== 'object') throw wrongFormatErr;
_.each(overrides, (localeString, key) => {
if (typeof localeString !== 'string') throw wrongFormatErr;
+ const locale = locales[langcode];
+
+ // Handles the error if an unknown language code is entered
+ if (locale === undefined) {
+ const possibleMatches = [];
+ let strippedLangcode = '';
+ if (langcode.includes('-')) {
+ strippedLangcode = langcode.split('-')[0];
+ }
+ for (const localeInEtherPad of Object.keys(locales)) {
+ if (localeInEtherPad.includes(strippedLangcode)) {
+ possibleMatches.push(localeInEtherPad);
+ }
+ }
+ throw new Error(`Language code ${langcode} is unknown. ` +
+ `Maybe you meant: ${possibleMatches}`);
+ }
+
locales[langcode][key] = localeString;
});
});
diff --git a/src/node/server.js b/src/node/server.js
index 04c622197..44f0a1e3d 100755
--- a/src/node/server.js
+++ b/src/node/server.js
@@ -49,6 +49,7 @@ const express = require('./hooks/express');
const hooks = require('../static/js/pluginfw/hooks');
const pluginDefs = require('../static/js/pluginfw/plugin_defs');
const plugins = require('../static/js/pluginfw/plugins');
+const installer = require('../static/js/pluginfw/installer');
const {Gate} = require('./utils/promises');
const stats = require('./stats');
@@ -139,6 +140,7 @@ exports.start = async () => {
}
await db.init();
+ await installer.checkForMigration();
await plugins.update();
const installedPlugins = Object.values(pluginDefs.plugins)
.filter((plugin) => plugin.package.name !== 'ep_etherpad-lite')
diff --git a/src/package-lock.json b/src/package-lock.json
index 346177459..1b2e08da4 100644
--- a/src/package-lock.json
+++ b/src/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "ep_etherpad-lite",
- "version": "1.9.6",
+ "version": "1.9.7",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -170,9 +170,9 @@
"integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg=="
},
"@jridgewell/trace-mapping": {
- "version": "0.3.20",
- "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz",
- "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==",
+ "version": "0.3.21",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.21.tgz",
+ "integrity": "sha512-SRfKmRe1KvYnxjEMtxEr+J4HIeMX5YBg/qhRHpxEIGjhX1rshcHlnFUE9K0GazhVKWM7B+nARSkV8LuvJdJ5/g==",
"requires": {
"@jridgewell/resolve-uri": "^3.1.0",
"@jridgewell/sourcemap-codec": "^1.4.14"
@@ -261,6 +261,22 @@
"integrity": "sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ==",
"dev": true
},
+ "@types/debug": {
+ "version": "4.1.12",
+ "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz",
+ "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==",
+ "requires": {
+ "@types/ms": "*"
+ }
+ },
+ "@types/fs-extra": {
+ "version": "9.0.13",
+ "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz",
+ "integrity": "sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==",
+ "requires": {
+ "@types/node": "*"
+ }
+ },
"@types/hast": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.1.tgz",
@@ -280,15 +296,20 @@
"integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
"dev": true
},
+ "@types/lockfile": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/@types/lockfile/-/lockfile-1.0.4.tgz",
+ "integrity": "sha512-Q8oFIHJHr+htLrTXN2FuZfg+WXVHQRwU/hC2GpUu+Q8e3FUM9EDkS2pE3R2AO1ZGu56f479ybdMCNF1DAu8cAQ=="
+ },
"@types/lodash": {
- "version": "4.14.199",
- "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.199.tgz",
- "integrity": "sha512-Vrjz5N5Ia4SEzWWgIVwnHNEnb1UE1XMkvY5DGXrAeOGE9imk0hgTHh5GyDjLDJi9OTCn9oo9dXH1uToK1VRfrg=="
+ "version": "4.14.202",
+ "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.202.tgz",
+ "integrity": "sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ=="
},
"@types/lodash.clonedeep": {
- "version": "4.5.7",
- "resolved": "https://registry.npmjs.org/@types/lodash.clonedeep/-/lodash.clonedeep-4.5.7.tgz",
- "integrity": "sha512-ccNqkPptFIXrpVqUECi60/DFxjNKsfoQxSQsgcBJCX/fuX1wgyQieojkcWH/KpE3xzLoWN/2k+ZeGqIN3paSvw==",
+ "version": "4.5.9",
+ "resolved": "https://registry.npmjs.org/@types/lodash.clonedeep/-/lodash.clonedeep-4.5.9.tgz",
+ "integrity": "sha512-19429mWC+FyaAhOLzsS8kZUsI+/GmBAQ0HFiCPsKGU+7pBXOQWhyrY6xNNDwUSX8SMZMJvuFVMF9O5dQOlQK9Q==",
"requires": {
"@types/lodash": "*"
}
@@ -301,17 +322,52 @@
"@types/unist": "*"
}
},
+ "@types/ms": {
+ "version": "0.7.34",
+ "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz",
+ "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g=="
+ },
+ "@types/node": {
+ "version": "20.10.6",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.6.tgz",
+ "integrity": "sha512-Vac8H+NlRNNlAmDfGUP7b5h/KA+AtWIzuXy0E6OyP8f1tCLYAtPvKRRDJjAPqhpCb0t6U2j7/xqAuLEebW2kiw==",
+ "requires": {
+ "undici-types": "~5.26.4"
+ }
+ },
+ "@types/node-fetch": {
+ "version": "2.6.10",
+ "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.10.tgz",
+ "integrity": "sha512-PPpPK6F9ALFTn59Ka3BaL+qGuipRfxNE8qVgkp0bVixeiR2c2/L+IVOiBdu9JhhT22sWnQEp6YyHGI2b2+CMcA==",
+ "requires": {
+ "@types/node": "*",
+ "form-data": "^4.0.0"
+ }
+ },
"@types/semver": {
"version": "7.5.3",
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.3.tgz",
- "integrity": "sha512-OxepLK9EuNEIPxWNME+C6WwbRAOOI2o2BaQEGzz5Lu2e4Z5eDnEo+/aVEDMIXywoJitJ7xWd641wrGLZdtwRyw==",
- "dev": true
+ "integrity": "sha512-OxepLK9EuNEIPxWNME+C6WwbRAOOI2o2BaQEGzz5Lu2e4Z5eDnEo+/aVEDMIXywoJitJ7xWd641wrGLZdtwRyw=="
+ },
+ "@types/tar": {
+ "version": "6.1.10",
+ "resolved": "https://registry.npmjs.org/@types/tar/-/tar-6.1.10.tgz",
+ "integrity": "sha512-60ZO+W0tRKJ3ggdzJKp75xKVlNogKYMqGvr2bMH/+k3T0BagfYTnbmVDFMJB1BFttz6yRgP5MDGP27eh7brrqw==",
+ "requires": {
+ "@types/node": "*",
+ "minipass": "^4.0.0"
+ }
},
"@types/unist": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.0.tgz",
"integrity": "sha512-MFETx3tbTjE7Uk6vvnWINA/1iJ7LuMdO4fcq8UfF0pRbj01aGLduVvQcRyswuACJdpnHgg8E3rQLhaRdNEJS0w=="
},
+ "@types/url-join": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/@types/url-join/-/url-join-4.0.1.tgz",
+ "integrity": "sha512-wDXw9LEEUHyV+7UWy7U315nrJGJ7p1BzaCxDpEoLr789Dk1WDVMMlf3iBfbG2F8NdWnYyFbtTxUn2ZNbm1Q4LQ=="
+ },
"@typescript-eslint/eslint-plugin": {
"version": "5.62.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz",
@@ -575,11 +631,6 @@
"integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==",
"dev": true
},
- "ansi-regex": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz",
- "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw=="
- },
"ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
@@ -716,11 +767,11 @@
"dev": true
},
"axios": {
- "version": "1.6.2",
- "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz",
- "integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==",
+ "version": "1.6.7",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.7.tgz",
+ "integrity": "sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==",
"requires": {
- "follow-redirects": "^1.15.0",
+ "follow-redirects": "^1.15.4",
"form-data": "^4.0.0",
"proxy-from-env": "^1.1.0"
}
@@ -903,6 +954,11 @@
}
}
},
+ "chownr": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
+ "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ=="
+ },
"clean-css": {
"version": "5.3.3",
"resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz",
@@ -1027,9 +1083,9 @@
}
},
"cssstyle": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-3.0.0.tgz",
- "integrity": "sha512-N4u2ABATi3Qplzf0hWbVCdjenim8F3ojEXpBDF5hBpjzW182MjNGLqfmQ0SkSPeQ+V86ZXgeH8aXj6kayd4jgg==",
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.0.1.tgz",
+ "integrity": "sha512-8ZYiJ3A/3OkDd093CBT/0UKDWry7ak4BdPTFP2+QEP7cmhouyq/Up709ASSj2cK02BbZiMgk7kYjZNS4QP5qrQ==",
"requires": {
"rrweb-cssom": "^0.6.0"
}
@@ -2105,9 +2161,9 @@
"integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ=="
},
"follow-redirects": {
- "version": "1.15.3",
- "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz",
- "integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q=="
+ "version": "1.15.5",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz",
+ "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw=="
},
"for-each": {
"version": "0.3.3",
@@ -2165,6 +2221,24 @@
}
}
},
+ "fs-minipass": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
+ "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
+ "requires": {
+ "minipass": "^3.0.0"
+ },
+ "dependencies": {
+ "minipass": {
+ "version": "3.3.6",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
+ "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
+ "requires": {
+ "yallist": "^4.0.0"
+ }
+ }
+ }
+ },
"fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
@@ -2889,11 +2963,11 @@
}
},
"jsdom": {
- "version": "23.0.1",
- "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-23.0.1.tgz",
- "integrity": "sha512-2i27vgvlUsGEBO9+/kJQRbtqtm+191b5zAZrU/UezVmnC2dlDAFLgDYJvAEi94T4kjsRKkezEtLQTgsNEsW2lQ==",
+ "version": "24.0.0",
+ "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-24.0.0.tgz",
+ "integrity": "sha512-UDS2NayCvmXSXVP6mpTj+73JnNQadZlr9N68189xib2tx5Mls7swlTNao26IoHv46BZJFvXygyRtyXd1feAk1A==",
"requires": {
- "cssstyle": "^3.0.0",
+ "cssstyle": "^4.0.1",
"data-urls": "^5.0.0",
"decimal.js": "^10.4.3",
"form-data": "^4.0.0",
@@ -2912,8 +2986,15 @@
"whatwg-encoding": "^3.1.1",
"whatwg-mimetype": "^4.0.0",
"whatwg-url": "^14.0.0",
- "ws": "^8.14.2",
+ "ws": "^8.16.0",
"xml-name-validator": "^5.0.0"
+ },
+ "dependencies": {
+ "ws": {
+ "version": "8.16.0",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz",
+ "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ=="
+ }
}
},
"json-buffer": {
@@ -3024,6 +3105,66 @@
"immediate": "~3.0.5"
}
},
+ "live-plugin-manager": {
+ "version": "0.18.1",
+ "resolved": "https://registry.npmjs.org/live-plugin-manager/-/live-plugin-manager-0.18.1.tgz",
+ "integrity": "sha512-GvLMSaZ1Cc18o91NiHLRuPXm1z7xDiUXUGgQ6jAwGM/x0FY8vXXHa/+LMNb2zrkAV2bWULCs0FEwX9yRsmFZmw==",
+ "requires": {
+ "@types/debug": "^4.1.7",
+ "@types/fs-extra": "^9.0.13",
+ "@types/lockfile": "^1.0.2",
+ "@types/node-fetch": "^2.5.12",
+ "@types/semver": "^7.3.9",
+ "@types/tar": "^6.1.1",
+ "@types/url-join": "4.0.1",
+ "debug": "^4.3.3",
+ "fs-extra": "^10.0.0",
+ "lockfile": "^1.0.4",
+ "node-fetch": "^2.6.6",
+ "semver": "^7.3.5",
+ "tar": "^6.1.11",
+ "url-join": "^4.0.1"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "4.3.4",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+ "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+ "requires": {
+ "ms": "2.1.2"
+ }
+ },
+ "fs-extra": {
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
+ "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
+ "requires": {
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^6.0.1",
+ "universalify": "^2.0.0"
+ }
+ },
+ "jsonfile": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
+ "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
+ "requires": {
+ "graceful-fs": "^4.1.6",
+ "universalify": "^2.0.0"
+ }
+ },
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+ },
+ "universalify": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
+ "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw=="
+ }
+ }
+ },
"locate-path": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
@@ -3033,6 +3174,14 @@
"p-locate": "^5.0.0"
}
},
+ "lockfile": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/lockfile/-/lockfile-1.0.4.tgz",
+ "integrity": "sha512-cvbTwETRfsFh4nHsL1eGWapU1XFi5Ot9E85sWAwia7Y7EgB7vfqcZhTKZ+l7hCGxSPoushMv5GKhT5PdLv03WA==",
+ "requires": {
+ "signal-exit": "^3.0.2"
+ }
+ },
"lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
@@ -3231,6 +3380,35 @@
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
"dev": true
},
+ "minipass": {
+ "version": "4.2.8",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.8.tgz",
+ "integrity": "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ=="
+ },
+ "minizlib": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
+ "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
+ "requires": {
+ "minipass": "^3.0.0",
+ "yallist": "^4.0.0"
+ },
+ "dependencies": {
+ "minipass": {
+ "version": "3.3.6",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
+ "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
+ "requires": {
+ "yallist": "^4.0.0"
+ }
+ }
+ }
+ },
+ "mkdirp": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
+ "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="
+ },
"mocha": {
"version": "10.2.0",
"resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz",
@@ -3444,6 +3622,35 @@
}
}
},
+ "node-fetch": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
+ "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
+ "requires": {
+ "whatwg-url": "^5.0.0"
+ },
+ "dependencies": {
+ "tr46": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
+ },
+ "webidl-conversions": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
+ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
+ },
+ "whatwg-url": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
+ "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
+ "requires": {
+ "tr46": "~0.0.3",
+ "webidl-conversions": "^3.0.0"
+ }
+ }
+ }
+ },
"nodeify": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/nodeify/-/nodeify-1.0.1.tgz",
@@ -3631,6 +3838,10 @@
"string-width": "^2.0.0"
}
},
+ "ansi-regex": {
+ "version": "2.1.1",
+ "bundled": true
+ },
"ansi-styles": {
"version": "3.2.1",
"bundled": true,
@@ -3880,8 +4091,7 @@
"dependencies": {
"ansi-regex": {
"version": "4.1.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz",
- "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g=="
+ "bundled": true
},
"is-fullwidth-code-point": {
"version": "2.0.0",
@@ -6182,6 +6392,10 @@
"strip-ansi": "^4.0.0"
},
"dependencies": {
+ "ansi-regex": {
+ "version": "3.0.0",
+ "bundled": true
+ },
"is-fullwidth-code-point": {
"version": "2.0.0",
"bundled": true
@@ -6217,13 +6431,6 @@
"bundled": true,
"requires": {
"ansi-regex": "^2.0.0"
- },
- "dependencies": {
- "ansi-regex": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
- "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA=="
- }
}
},
"strip-eof": {
@@ -6543,8 +6750,7 @@
"dependencies": {
"ansi-regex": {
"version": "4.1.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz",
- "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g=="
+ "bundled": true
},
"is-fullwidth-code-point": {
"version": "2.0.0",
@@ -6615,9 +6821,8 @@
},
"dependencies": {
"ansi-regex": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz",
- "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g=="
+ "version": "4.1.0",
+ "bundled": true
},
"find-up": {
"version": "3.0.0",
@@ -6779,9 +6984,9 @@
}
},
"openapi-backend": {
- "version": "5.10.5",
- "resolved": "https://registry.npmjs.org/openapi-backend/-/openapi-backend-5.10.5.tgz",
- "integrity": "sha512-ivZfL0Lwj7rRctCqxAquGy4j/VcdUXUvDsEVM3NG/2jDuvYT2dS+sf9ntGo5vv4hkOnkWgPnR6HxHp7NPexqAA==",
+ "version": "5.10.6",
+ "resolved": "https://registry.npmjs.org/openapi-backend/-/openapi-backend-5.10.6.tgz",
+ "integrity": "sha512-vTjBRys/O4JIHdlRHUKZ7pxS+gwIJreAAU9dvYRFrImtPzQ5qxm5a6B8BTVT9m6I8RGGsShJv35MAc3Tu2/y/A==",
"requires": {
"@apidevtools/json-schema-ref-parser": "^11.1.0",
"ajv": "^8.6.2",
@@ -7038,9 +7243,9 @@
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
},
"rate-limiter-flexible": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/rate-limiter-flexible/-/rate-limiter-flexible-4.0.0.tgz",
- "integrity": "sha512-SkA18LEPqJJKHixi6E7tzBKTXbj9gu5wPyfTykPVRZR5JGSw0dMCjtZsjlfuabVY940pu28Wu87NZN4FhztnyQ=="
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/rate-limiter-flexible/-/rate-limiter-flexible-4.0.1.tgz",
+ "integrity": "sha512-2/dGHpDFpeA0+755oUkW+EKyklqLS9lu0go9pDsbhqQjZcxfRyJ6LA4JI0+HAdZ2bemD/oOjUeZQB2lCZqXQfQ=="
},
"raw-body": {
"version": "2.5.1",
@@ -7272,9 +7477,9 @@
"integrity": "sha512-5qfoAgfRWS1sUn+fUJtdbbqM1BD/LoQGa+smPTDjf9OqHyuJqi6ewtbYL0+V1S1RaU6OCOCMWGZocIfz2YK4uw=="
},
"selenium-webdriver": {
- "version": "4.16.0",
- "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-4.16.0.tgz",
- "integrity": "sha512-IbqpRpfGE7JDGgXHJeWuCqT/tUqnLvZ14csSwt+S8o4nJo3RtQoE9VR4jB47tP/A8ArkYsh/THuMY6kyRP6kuA==",
+ "version": "4.17.0",
+ "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-4.17.0.tgz",
+ "integrity": "sha512-e2E+2XBlGepzwgFbyQfSwo9Cbj6G5fFfs9MzAS00nC99EewmcS2rwn2MwtgfP7I5p1e7DYv4HQJXtWedsu6DvA==",
"dev": true,
"requires": {
"jszip": "^3.10.1",
@@ -7388,6 +7593,11 @@
"object-inspect": "^1.9.0"
}
},
+ "signal-exit": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
+ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="
+ },
"sinon": {
"version": "17.0.1",
"resolved": "https://registry.npmjs.org/sinon/-/sinon-17.0.1.tgz",
@@ -7730,13 +7940,13 @@
}
},
"supertest": {
- "version": "6.3.3",
- "resolved": "https://registry.npmjs.org/supertest/-/supertest-6.3.3.tgz",
- "integrity": "sha512-EMCG6G8gDu5qEqRQ3JjjPs6+FYT1a7Hv5ApHvtSghmOFJYtsU5S+pSb6Y2EUeCEY3CmEL3mmQ8YWlPOzQomabA==",
+ "version": "6.3.4",
+ "resolved": "https://registry.npmjs.org/supertest/-/supertest-6.3.4.tgz",
+ "integrity": "sha512-erY3HFDG0dPnhw4U+udPfrzXa4xhSG+n4rxfRuZWCUvjFWwKl+OxWf/7zk50s84/fAAs7vf5QAb9uRa0cCykxw==",
"dev": true,
"requires": {
"methods": "^1.1.2",
- "superagent": "^8.0.5"
+ "superagent": "^8.1.2"
}
},
"supports-color": {
@@ -7769,10 +7979,30 @@
"integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
"dev": true
},
+ "tar": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.0.tgz",
+ "integrity": "sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==",
+ "requires": {
+ "chownr": "^2.0.0",
+ "fs-minipass": "^2.0.0",
+ "minipass": "^5.0.0",
+ "minizlib": "^2.1.1",
+ "mkdirp": "^1.0.3",
+ "yallist": "^4.0.0"
+ },
+ "dependencies": {
+ "minipass": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz",
+ "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ=="
+ }
+ }
+ },
"terser": {
- "version": "5.26.0",
- "resolved": "https://registry.npmjs.org/terser/-/terser-5.26.0.tgz",
- "integrity": "sha512-dytTGoE2oHgbNV9nTzgBEPaqAWvcJNl66VZ0BkJqlvp71IjO8CxdBx/ykCNb47cLnCmCvRZ6ZR0tLkqvZCdVBQ==",
+ "version": "5.27.0",
+ "resolved": "https://registry.npmjs.org/terser/-/terser-5.27.0.tgz",
+ "integrity": "sha512-bi1HRwVRskAjheeYl291n3JC4GgO/Ty4z1nVs5AAsmonJulGxpSektecnNedrwK9C7vpvVtcX3cw00VSLt7U2A==",
"requires": {
"@jridgewell/source-map": "^0.3.3",
"acorn": "^8.8.2",
@@ -8002,9 +8232,9 @@
"dev": true
},
"ueberdb2": {
- "version": "4.2.44",
- "resolved": "https://registry.npmjs.org/ueberdb2/-/ueberdb2-4.2.44.tgz",
- "integrity": "sha512-OeVKUtchoRg1O4/sza9PqU1igOfpifqaDyGNuotmG1J6q0uG+0BnUtGNeaOfgoYbDjlI0iPShy792Px6BHr1rw=="
+ "version": "4.2.50",
+ "resolved": "https://registry.npmjs.org/ueberdb2/-/ueberdb2-4.2.50.tgz",
+ "integrity": "sha512-XOiWxmDHhoCNLSrepIJa+kdRBXABuI5ZXQhXpmT2Z8qK3EbipcrAeQBWDQXfcnnHftmzaR3NY7zil76biCN/GQ=="
},
"uid-safe": {
"version": "2.1.5",
@@ -8031,6 +8261,11 @@
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz",
"integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A=="
},
+ "undici-types": {
+ "version": "5.26.5",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
+ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="
+ },
"unified": {
"version": "11.0.3",
"resolved": "https://registry.npmjs.org/unified/-/unified-11.0.3.tgz",
@@ -8111,6 +8346,11 @@
"punycode": "^2.1.0"
}
},
+ "url-join": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz",
+ "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA=="
+ },
"url-parse": {
"version": "1.5.10",
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
@@ -8271,9 +8511,10 @@
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
},
"ws": {
- "version": "8.14.2",
- "resolved": "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz",
- "integrity": "sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g=="
+ "version": "8.16.0",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz",
+ "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==",
+ "dev": true
},
"wtfnode": {
"version": "0.9.1",
diff --git a/src/package.json b/src/package.json
index a3aa49de1..77b313f2b 100644
--- a/src/package.json
+++ b/src/package.json
@@ -31,7 +31,7 @@
],
"dependencies": {
"async": "^3.2.5",
- "axios": "^1.6.2",
+ "axios": "^1.6.7",
"clean-css": "^5.3.3",
"cookie-parser": "^1.4.6",
"cross-spawn": "^7.0.3",
@@ -46,17 +46,18 @@
"formidable": "^3.5.1",
"http-errors": "^2.0.0",
"js-cookie": "^3.0.5",
- "jsdom": "^23.0.1",
+ "jsdom": "^24.0.0",
"jsonminify": "0.4.2",
"languages4translatewiki": "0.1.3",
+ "live-plugin-manager": "^0.18.1",
"lodash.clonedeep": "4.5.0",
"log4js": "^6.9.1",
"measured-core": "^2.0.0",
"mime-types": "^2.1.35",
"npm": "^6.14.18",
- "openapi-backend": "^5.10.5",
+ "openapi-backend": "^5.10.6",
"proxy-addr": "^2.0.7",
- "rate-limiter-flexible": "^4.0.0",
+ "rate-limiter-flexible": "^4.0.1",
"rehype": "^13.0.1",
"rehype-minify-whitespace": "^6.0.0",
"resolve": "1.22.8",
@@ -64,10 +65,10 @@
"semver": "^7.5.4",
"socket.io": "^2.5.0",
"superagent": "^8.1.2",
- "terser": "^5.26.0",
+ "terser": "^5.27.0",
"threads": "^1.7.0",
"tinycon": "0.6.8",
- "ueberdb2": "^4.2.44",
+ "ueberdb2": "^4.2.50",
"underscore": "1.13.6",
"unorm": "1.6.0",
"wtfnode": "^0.9.1"
@@ -85,11 +86,11 @@
"mocha-froth": "^0.2.10",
"nodeify": "^1.0.1",
"openapi-schema-validation": "^0.4.2",
- "selenium-webdriver": "^4.16.0",
+ "selenium-webdriver": "^4.17.0",
"set-cookie-parser": "^2.6.0",
"sinon": "^17.0.1",
"split-grid": "^1.0.11",
- "supertest": "^6.3.3",
+ "supertest": "^6.3.4",
"typescript": "^5.3.3"
},
"engines": {
@@ -105,6 +106,6 @@
"test": "mocha --timeout 120000 --recursive tests/backend/specs ../node_modules/ep_*/static/tests/backend/specs",
"test-container": "mocha --timeout 5000 tests/container/specs/api"
},
- "version": "1.9.6",
+ "version": "1.9.7",
"license": "Apache-2.0"
}
diff --git a/src/static/js/pluginfw/installer.js b/src/static/js/pluginfw/installer.js
index d0e9dab6c..09e329c3e 100644
--- a/src/static/js/pluginfw/installer.js
+++ b/src/static/js/pluginfw/installer.js
@@ -6,10 +6,19 @@ const hooks = require('./hooks');
const runCmd = require('../../../node/utils/run_cmd');
const settings = require('../../../node/utils/Settings');
const axios = require('axios');
-
+const {PluginManager} = require("live-plugin-manager");
+const {promises: fs} = require("fs");
+const path = require("path");
+const {findEtherpadRoot} = require("../../../node/utils/AbsolutePaths");
const logger = log4js.getLogger('plugins');
+exports.manager = new PluginManager();
+
+const installedPluginsPath = path.join(settings.root, 'var/installed_plugins.json');
+
const onAllTasksFinished = async () => {
+ await plugins.update();
+ await persistInstalledPlugins();
settings.reloadSettings();
await hooks.aCallAll('loadSettings', {settings});
await hooks.aCallAll('restartServer');
@@ -31,41 +40,74 @@ const wrapTaskCb = (cb) => {
};
};
+const migratePluginsFromNodeModules = async () => {
+ logger.info('start migration of plugins in node_modules')
+ // Notes:
+ // * Do not pass `--prod` otherwise `npm ls` will fail if there is no `package.json`.
+ // * The `--no-production` flag is required (or the `NODE_ENV` environment variable must be
+ // unset or set to `development`) because otherwise `npm ls` will not mention any packages
+ // that are not included in `package.json` (which is expected to not exist).
+ const cmd = ['npm', 'ls', '--long', '--json', '--depth=0', '--no-production'];
+ const {dependencies = {}} = JSON.parse(await runCmd(cmd, {stdio: [null, 'string']}));
+ await Promise.all(Object.entries(dependencies).map(async ([pkg, info]) => {
+ if (pkg.startsWith(plugins.prefix) && pkg !== 'ep_etherpad-lite') {
+ if (!info._resolved) {
+ // Install from node_modules directory
+ await exports.manager.installFromPath(`${findEtherpadRoot()}/node_modules/${pkg}`);
+ } else {
+ await exports.manager.install(pkg);
+ }
+ }
+ }));
+ await persistInstalledPlugins();
+};
+
+exports.checkForMigration = async () => {
+ logger.info('check installed plugins for migration');
+
+ try {
+ await fs.access(installedPluginsPath, fs.constants.F_OK);
+ } catch (err) {
+ await migratePluginsFromNodeModules();
+ }
+
+ const fileContent = await fs.readFile(installedPluginsPath);
+ const installedPlugins = JSON.parse(fileContent.toString());
+
+ for (const plugin of installedPlugins.plugins) {
+ if (plugin.name.startsWith(plugins.prefix) && plugin.name !== 'ep_etherpad-lite') {
+ await exports.manager.install(plugin.name, plugin.version)
+ }
+ }
+};
+
+const persistInstalledPlugins = async () => {
+ let installedPlugins = { plugins: []};
+ for (const pkg of Object.values(await plugins.getPackages())) {
+ installedPlugins.plugins.push({
+ name: pkg.name,
+ version: pkg.version,
+ })
+ }
+ installedPlugins.plugins = [...new Set(installedPlugins.plugins)];
+ await fs.writeFile(installedPluginsPath, JSON.stringify(installedPlugins));
+}
+
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]);
- } catch (err) {
- logger.error(`Failed to uninstall plugin ${pluginName}`);
- cb(err || new Error(err));
- throw err;
- }
+ await exports.manager.uninstall(pluginName);
logger.info(`Successfully uninstalled plugin ${pluginName}`);
await hooks.aCallAll('pluginUninstall', {pluginName});
- await plugins.update();
cb(null);
};
exports.install = async (pluginName, cb = null) => {
cb = wrapTaskCb(cb);
logger.info(`Installing 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', 'install', '--no-save', '--legacy-peer-deps', pluginName]);
- } catch (err) {
- logger.error(`Failed to install plugin ${pluginName}`);
- cb(err || new Error(err));
- throw err;
- }
+ await exports.manager.install(pluginName);
logger.info(`Successfully installed plugin ${pluginName}`);
await hooks.aCallAll('pluginInstall', {pluginName});
- await plugins.update();
cb(null);
};
@@ -76,22 +118,21 @@ exports.getAvailablePlugins = (maxCacheAge) => {
const nowTimestamp = Math.round(Date.now() / 1000);
return new Promise(async (resolve, reject) => {
- // check cache age before making any request
- if (exports.availablePlugins && maxCacheAge && (nowTimestamp - cacheTimestamp) <= maxCacheAge) {
- return resolve(exports.availablePlugins);
- }
+ // check cache age before making any request
+ if (exports.availablePlugins && maxCacheAge && (nowTimestamp - cacheTimestamp) <= maxCacheAge) {
+ return resolve(exports.availablePlugins);
+ }
- await axios.get('https://static.etherpad.org/plugins.json', {headers: headers})
- .then(pluginsLoaded => {
- exports.availablePlugins = pluginsLoaded.data;
- cacheTimestamp = nowTimestamp;
- resolve(exports.availablePlugins);
- })
+ await axios.get('https://static.etherpad.org/plugins.json', {headers: headers})
+ .then((pluginsLoaded) => {
+ exports.availablePlugins = pluginsLoaded.data;
+ cacheTimestamp = nowTimestamp;
+ resolve(exports.availablePlugins);})
.catch(async err => {
return reject(err);
});
- })
-}
+ });
+};
exports.search = (searchTerm, maxCacheAge) => exports.getAvailablePlugins(maxCacheAge).then(
@@ -108,8 +149,8 @@ exports.search = (searchTerm, maxCacheAge) => exports.getAvailablePlugins(maxCac
if (pluginName.indexOf(plugins.prefix) !== 0) continue;
if (searchTerm && !~results[pluginName].name.toLowerCase().indexOf(searchTerm) &&
- (typeof results[pluginName].description !== 'undefined' &&
- !~results[pluginName].description.toLowerCase().indexOf(searchTerm))
+ (typeof results[pluginName].description !== 'undefined' &&
+ !~results[pluginName].description.toLowerCase().indexOf(searchTerm))
) {
if (typeof results[pluginName].description === 'undefined') {
logger.debug(`plugin without Description: ${results[pluginName].name}`);
diff --git a/src/static/js/pluginfw/plugins.js b/src/static/js/pluginfw/plugins.js
index ec3cfaa92..179e016f9 100644
--- a/src/static/js/pluginfw/plugins.js
+++ b/src/static/js/pluginfw/plugins.js
@@ -8,6 +8,8 @@ const runCmd = require('../../../node/utils/run_cmd');
const tsort = require('./tsort');
const pluginUtils = require('./shared');
const defs = require('./plugin_defs');
+const {manager} = require('./installer');
+const settings = require("../../../node/utils/Settings");
const logger = log4js.getLogger('plugins');
@@ -105,22 +107,26 @@ exports.update = async () => {
};
exports.getPackages = async () => {
- logger.info('Running npm to get a list of installed plugins...');
- // Notes:
- // * Do not pass `--prod` otherwise `npm ls` will fail if there is no `package.json`.
- // * The `--no-production` flag is required (or the `NODE_ENV` environment variable must be
- // unset or set to `development`) because otherwise `npm ls` will not mention any packages
- // that are not included in `package.json` (which is expected to not exist).
- const cmd = ['npm', 'ls', '--long', '--json', '--depth=0', '--no-production'];
- const {dependencies = {}} = JSON.parse(await runCmd(cmd, {stdio: [null, 'string']}));
- await Promise.all(Object.entries(dependencies).map(async ([pkg, info]) => {
- if (!pkg.startsWith(exports.prefix)) {
- delete dependencies[pkg];
- return;
+ let plugins = manager.list()
+ let newDependencies = {}
+
+ for (const plugin of plugins) {
+ if (!plugin.name.startsWith(exports.prefix)) {
+ continue;
}
- info.realPath = await fs.realpath(info.path);
- }));
- return dependencies;
+ plugin.realPath = await fs.realpath(plugin.location);
+ plugin.path = plugin.realPath;
+ newDependencies[plugin.name] = plugin
+ }
+
+ newDependencies['ep_etherpad-lite'] = {
+ name: 'ep_etherpad-lite',
+ version: settings.getEpVersion(),
+ path: path.join(settings.root, 'node_modules/ep_etherpad-lite'),
+ realPath: path.join(settings.root, 'src'),
+ }
+
+ return newDependencies;
};
const loadPlugin = async (packages, pluginName, plugins, parts) => {
diff --git a/src/tests/backend/specs/favicon.js b/src/tests/backend/specs/favicon.js
index a5e3095de..98c308061 100644
--- a/src/tests/backend/specs/favicon.js
+++ b/src/tests/backend/specs/favicon.js
@@ -51,6 +51,12 @@ describe(__filename, function () {
assert(gotIcon.equals(wantCustomIcon));
});
+ it('uses custom favicon from url', async function () {
+ settings.favicon = 'https://etherpad.org/favicon.ico';
+ await agent.get('/favicon.ico')
+ .expect(302);
+ });
+
it('uses custom favicon if set (absolute pathname)', async function () {
settings.favicon = path.join(__dirname, 'favicon-test-custom.png');
assert(path.isAbsolute(settings.favicon));
diff --git a/var/.gitignore b/var/.gitignore
index 91f8c0959..1dea48383 100644
--- a/var/.gitignore
+++ b/var/.gitignore
@@ -1,2 +1,3 @@
sqlite.db
minified*
+installed_plugins.json
\ No newline at end of file