diff --git a/.dockerignore b/.dockerignore index d8d3a3ebe..f7accabfd 100644 --- a/.dockerignore +++ b/.dockerignore @@ -24,3 +24,4 @@ Dockerfile settings.json src/node_modules +admin/node_modules diff --git a/.github/workflows/backend-tests.yml b/.github/workflows/backend-tests.yml index f41f5ac5e..0dd1000d8 100644 --- a/.github/workflows/backend-tests.yml +++ b/.github/workflows/backend-tests.yml @@ -54,6 +54,12 @@ jobs: - name: Install all dependencies and symlink for ep_etherpad-lite run: bin/installDeps.sh + - name: Install admin ui + working-directory: admin + run: pnpm install + - name: Build admin ui + working-directory: admin + run: pnpm build - name: Run the backend tests run: pnpm test @@ -105,6 +111,12 @@ jobs: - name: Install all dependencies and symlink for ep_etherpad-lite run: bin/installDeps.sh + - name: Install admin ui + working-directory: admin + run: pnpm install + - name: Build admin ui + working-directory: admin + run: pnpm build - name: Install Etherpad plugins run: > @@ -163,6 +175,12 @@ jobs: - name: Install all dependencies and symlink for ep_etherpad-lite run: bin/installOnWindows.bat + - name: Install admin ui + working-directory: admin + run: pnpm install + - name: Build admin ui + working-directory: admin + run: pnpm build - name: Fix up the settings.json run: | @@ -207,6 +225,12 @@ jobs: ${{ runner.os }}-pnpm-store- - name: Only install direct dependencies run: pnpm config set auto-install-peers false + - name: Install admin ui + working-directory: admin + run: pnpm install + - name: Build admin ui + working-directory: admin + run: pnpm build - name: Install Etherpad plugins # The --legacy-peer-deps flag is required to work around a bug in npm diff --git a/.github/workflows/frontend-admin-tests.yml b/.github/workflows/frontend-admin-tests.yml index aa1c4e70a..37c8ede08 100644 --- a/.github/workflows/frontend-admin-tests.yml +++ b/.github/workflows/frontend-admin-tests.yml @@ -12,7 +12,6 @@ jobs: name: with plugins runs-on: ubuntu-latest -# node: [16, 19, 20] >> Disabled node 16 and 18 because they do not work strategy: fail-fast: false matrix: @@ -83,11 +82,11 @@ jobs: run: "sed -i 's/\"enableAdminUITests\": false/\"enableAdminUITests\": true,\\n\"users\":{\"admin\":{\"password\":\"changeme\",\"is_admin\":true}}/' settings.json" - name: increase maxHttpBufferSize - run: "sed -i 's/\"maxHttpBufferSize\": 10000/\"maxHttpBufferSize\": 100000/' settings.json" + run: "sed -i 's/\"maxHttpBufferSize\": 10000/\"maxHttpBufferSize\": 10000000/' settings.json" - name: Disable import/export rate limiting run: | - sed -e '/^ *"importExportRateLimiting":/,/^ *\}/ s/"max":.*/"max": 1000000/' -i settings.json + sed -e '/^ *"importExportRateLimiting":/,/^ *\}/ s/"max":.*/"max": 100000000/' -i settings.json - name: Remove standard frontend test files, so only admin tests are run run: mv src/tests/frontend/specs/* /tmp && mv /tmp/admin*.js src/tests/frontend/specs diff --git a/.gitignore b/.gitignore index 38e2889d9..2a8335497 100644 --- a/.gitignore +++ b/.gitignore @@ -23,4 +23,5 @@ out/ /src/bin/etherpad-1.deb /src/bin/node.exe plugin_packages -pnpm-lock.yaml \ No newline at end of file +pnpm-lock.yaml +/src/templates/admin diff --git a/Dockerfile b/Dockerfile index 7cd8105d9..35e4665b2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,6 +4,13 @@ # # Author: muxator +FROM node:alpine as adminBuild + +WORKDIR /opt/etherpad-lite +COPY ./admin ./admin +RUN cd ./admin && npm install -g pnpm && pnpm install && pnpm run build --outDir ./dist + + FROM node:alpine as build LABEL maintainer="Etherpad team, https://github.com/ether/etherpad-lite" @@ -99,16 +106,18 @@ COPY --chown=etherpad:etherpad ./pnpm-workspace.yaml ./package.json ./ FROM build as development COPY --chown=etherpad:etherpad ./src/package.json .npmrc ./src/pnpm-lock.yaml ./src/ +COPY --chown=etherpad:etherpad --from=adminBuild /opt/etherpad-lite/admin/dist ./src/templates/admin RUN bin/installDeps.sh && { [ -z "${ETHERPAD_PLUGINS}" ] || \ pnpm install --workspace-root ${ETHERPAD_PLUGINS}; } - + FROM build as production ENV NODE_ENV=production ENV ETHERPAD_PRODUCTION=true COPY --chown=etherpad:etherpad ./src ./src +COPY --chown=etherpad:etherpad --from=adminBuild /opt/etherpad-lite/admin/dist ./src/templates/admin RUN bin/installDeps.sh && { [ -z "${ETHERPAD_PLUGINS}" ] || \ pnpm install --workspace-root ${ETHERPAD_PLUGINS}; } && \ diff --git a/admin/index.html b/admin/index.html index daff73e73..8863894ed 100644 --- a/admin/index.html +++ b/admin/index.html @@ -2,9 +2,9 @@
- -{ setSearchParams({ ...searchParams, sortBy: 'lastEdited', ascending: !searchParams.ascending }) - }}>Users | + }}>{ setSearchParams({ ...searchParams, sortBy: 'userCount', ascending: !searchParams.ascending }) - }}>Last Edited | + }}>{ setSearchParams({ ...searchParams, @@ -100,7 +131,7 @@ export const PadPage = ()=>{ ascending: !searchParams.ascending }) }}>Revision number | -Actions | +
+ setPadToDelete(pad.padName)
+ setDeleteDialog(true)
+ }}> ${plugins.formatPlugins().replace(/, /g, '\n')}`, - installedParts: ` ${plugins.formatParts()}`, - installedServerHooks: ` ${plugins.formatHooks('hooks', true)} `,
- installedClientHooks: `${plugins.formatHooks('client_hooks', true)} `,
- latestVersion: UpdateCheck.getLatestVersion(),
- req,
- }));
- });
-
- return cb();
-};
exports.socketio = (hookName:string, args:ArgsExpressType, cb:Function) => {
const io = args.io.of('/pluginfw/installer');
diff --git a/src/node/hooks/express/adminsettings.ts b/src/node/hooks/express/adminsettings.ts
index a7d3b8585..03258c584 100644
--- a/src/node/hooks/express/adminsettings.ts
+++ b/src/node/hooks/express/adminsettings.ts
@@ -13,16 +13,6 @@ const UpdateCheck = require('../../utils/UpdateCheck');
const padManager = require('../../db/PadManager');
const api = require('../../db/API');
-exports.expressCreateServer = (hookName:string, {app}:any) => {
- app.get('/admin/settings', (req:any, res:any) => {
- res.send(eejs.require('ep_etherpad-lite/templates/admin/settings.html', {
- req,
- settings: '',
- errors: [],
- }));
- });
-};
-
const queryPadLimit = 12;
diff --git a/src/node/hooks/express/webaccess.ts b/src/node/hooks/express/webaccess.ts
index 43bdea5fc..0034f87c8 100644
--- a/src/node/hooks/express/webaccess.ts
+++ b/src/node/hooks/express/webaccess.ts
@@ -50,7 +50,7 @@ exports.userCanModify = (padId: string, req: SocketClientRequest) => {
exports.authnFailureDelayMs = 1000;
const checkAccess = async (req:any, res:any, next: Function) => {
- const requireAdmin = req.path.toLowerCase().startsWith('/admin');
+ const requireAdmin = req.path.toLowerCase().startsWith('/admin-auth');
// ///////////////////////////////////////////////////////////////////////////////////////////////
// Step 1: Check the preAuthorize hook for early permit/deny (permit is only allowed for non-admin
@@ -126,7 +126,13 @@ const checkAccess = async (req:any, res:any, next: Function) => {
// completed, or maybe different credentials are required), go to the next step.
// ///////////////////////////////////////////////////////////////////////////////////////////////
- if (await authorize()) return next();
+ if (await authorize()) {
+ if(requireAdmin) {
+ res.status(200).send('Authorized')
+ return
+ }
+ return next();
+ }
// ///////////////////////////////////////////////////////////////////////////////////////////////
// Step 3: Authenticate the user. (Or, if already logged in, reauthenticate with different
@@ -163,7 +169,7 @@ const checkAccess = async (req:any, res:any, next: Function) => {
if (await aCallFirst0('authnFailure', {req, res})) return;
if (await aCallFirst0('authFailure', {req, res, next})) return;
// No plugin handled the authentication failure. Fall back to basic authentication.
- res.header('WWW-Authenticate', 'Basic realm="Protected Area"');
+ //res.header('WWW-Authenticate', 'Basic realm="Protected Area"');
// Delay the error response for 1s to slow down brute force attacks.
await new Promise((resolve) => setTimeout(resolve, exports.authnFailureDelayMs));
res.status(401).send('Authentication Required');
@@ -188,7 +194,13 @@ const checkAccess = async (req:any, res:any, next: Function) => {
// a login page).
// ///////////////////////////////////////////////////////////////////////////////////////////////
- if (await authorize()) return next();
+ const auth = await authorize()
+ if (auth && !requireAdmin) return next();
+ if(auth && requireAdmin) {
+ res.status(200).send('Authorized')
+ return
+ }
+
if (await aCallFirst0('authzFailure', {req, res})) return;
if (await aCallFirst0('authFailure', {req, res, next})) return;
// No plugin handled the authorization failure.
diff --git a/src/static/js/admin/jquery.autosize.js b/src/static/js/admin/jquery.autosize.js
deleted file mode 100644
index a94ef3cde..000000000
--- a/src/static/js/admin/jquery.autosize.js
+++ /dev/null
@@ -1,180 +0,0 @@
-// Autosize 1.13 - jQuery plugin for textareas
-// (c) 2012 Jack Moore - jacklmoore.com
-// license: www.opensource.org/licenses/mit-license.php
-
-(function ($) {
- var
- defaults = {
- className: 'autosizejs',
- append: "",
- callback: false
- },
- hidden = 'hidden',
- borderBox = 'border-box',
- lineHeight = 'lineHeight',
- copy = '',
- // line-height is omitted because IE7/IE8 doesn't return the correct value.
- copyStyle = [
- 'fontFamily',
- 'fontSize',
- 'fontWeight',
- 'fontStyle',
- 'letterSpacing',
- 'textTransform',
- 'wordSpacing',
- 'textIndent'
- ],
- oninput = 'oninput',
- onpropertychange = 'onpropertychange',
- test = $(copy)[0];
-
- // For testing support in old FireFox
- test.setAttribute(oninput, "return");
-
- if ($.isFunction(test[oninput]) || onpropertychange in test) {
-
- // test that line-height can be accurately copied to avoid
- // incorrect value reporting in old IE and old Opera
- $(test).css(lineHeight, '99px');
- if ($(test).css(lineHeight) === '99px') {
- copyStyle.push(lineHeight);
- }
-
- $.fn.autosize = function (options) {
- options = $.extend({}, defaults, options || {});
-
- return this.each(function () {
- var
- ta = this,
- $ta = $(ta),
- mirror,
- minHeight = $ta.height(),
- maxHeight = parseInt($ta.css('maxHeight'), 10),
- active,
- i = copyStyle.length,
- resize,
- boxOffset = 0,
- value = ta.value,
- callback = $.isFunction(options.callback);
-
- if ($ta.css('box-sizing') === borderBox || $ta.css('-moz-box-sizing') === borderBox || $ta.css('-webkit-box-sizing') === borderBox){
- boxOffset = $ta.outerHeight() - $ta.height();
- }
-
- if ($ta.data('mirror') || $ta.data('ismirror')) {
- // if autosize has already been applied, exit.
- // if autosize is being applied to a mirror element, exit.
- return;
- } else {
- mirror = $(copy).data('ismirror', true).addClass(options.className)[0];
-
- resize = $ta.css('resize') === 'none' ? 'none' : 'horizontal';
-
- $ta.data('mirror', $(mirror)).css({
- overflow: hidden,
- overflowY: hidden,
- wordWrap: 'break-word',
- resize: resize
- });
- }
-
- // Opera returns '-1px' when max-height is set to 'none'.
- maxHeight = maxHeight && maxHeight > 0 ? maxHeight : 9e4;
-
- // Using mainly bare JS in this function because it is going
- // to fire very often while typing, and needs to very efficient.
- function adjust() {
- var height, overflow, original;
-
- // the active flag keeps IE from tripping all over itself. Otherwise
- // actions in the adjust function will cause IE to call adjust again.
- if (!active) {
- active = true;
- mirror.value = ta.value + options.append;
- mirror.style.overflowY = ta.style.overflowY;
- original = parseInt(ta.style.height,10);
-
- // Update the width in case the original textarea width has changed
- mirror.style.width = $ta.css('width');
-
- // Needed for IE to reliably return the correct scrollHeight
- mirror.scrollTop = 0;
-
- // Set a very high value for scrollTop to be sure the
- // mirror is scrolled all the way to the bottom.
- mirror.scrollTop = 9e4;
-
- height = mirror.scrollTop;
- overflow = hidden;
- if (height > maxHeight) {
- height = maxHeight;
- overflow = 'scroll';
- } else if (height < minHeight) {
- height = minHeight;
- }
- height += boxOffset;
- ta.style.overflowY = overflow;
-
- if (original !== height) {
- ta.style.height = height + 'px';
- if (callback) {
- options.callback.call(ta);
- }
- }
-
- // This small timeout gives IE a chance to draw it's scrollbar
- // before adjust can be run again (prevents an infinite loop).
- setTimeout(function () {
- active = false;
- }, 1);
- }
- }
-
- // mirror is a duplicate textarea located off-screen that
- // is automatically updated to contain the same text as the
- // original textarea. mirror always has a height of 0.
- // This gives a cross-browser supported way getting the actual
- // height of the text, through the scrollTop property.
- while (i--) {
- mirror.style[copyStyle[i]] = $ta.css(copyStyle[i]);
- }
-
- $('body').append(mirror);
-
- if (onpropertychange in ta) {
- if (oninput in ta) {
- // Detects IE9. IE9 does not fire onpropertychange or oninput for deletions,
- // so binding to onkeyup to catch most of those occassions. There is no way that I
- // know of to detect something like 'cut' in IE9.
- ta[oninput] = ta.onkeyup = adjust;
- } else {
- // IE7 / IE8
- ta[onpropertychange] = adjust;
- }
- } else {
- // Modern Browsers
- ta[oninput] = adjust;
-
- // The textarea overflow is now hidden. But Chrome doesn't reflow the text after the scrollbars are removed.
- // This is a hack to get Chrome to reflow it's text.
- ta.value = '';
- ta.value = value;
- }
-
- $(window).resize(adjust);
-
- // Allow for manual triggering if needed.
- $ta.on('autosize', adjust);
-
- // Call adjust in case the textarea already contains text.
- adjust();
- });
- };
- } else {
- // Makes no changes for older browsers (FireFox3- and Safari4-)
- $.fn.autosize = function (callback) {
- return this;
- };
- }
-
-}(jQuery));
\ No newline at end of file
diff --git a/src/static/js/admin/minify.json.js b/src/static/js/admin/minify.json.js
deleted file mode 100644
index 4edbd6e1d..000000000
--- a/src/static/js/admin/minify.json.js
+++ /dev/null
@@ -1,61 +0,0 @@
-/*! JSON.minify()
- v0.1 (c) Kyle Simpson
- MIT License
-*/
-
-(function(global){
- if (typeof global.JSON == "undefined" || !global.JSON) {
- global.JSON = {};
- }
-
- global.JSON.minify = function(json) {
-
- var tokenizer = /"|(\/\*)|(\*\/)|(\/\/)|\n|\r/g,
- in_string = false,
- in_multiline_comment = false,
- in_singleline_comment = false,
- tmp, tmp2, new_str = [], ns = 0, from = 0, lc, rc
- ;
-
- tokenizer.lastIndex = 0;
-
- while (tmp = tokenizer.exec(json)) {
- lc = RegExp.leftContext;
- rc = RegExp.rightContext;
- if (!in_multiline_comment && !in_singleline_comment) {
- tmp2 = lc.substring(from);
- if (!in_string) {
- tmp2 = tmp2.replace(/(\n|\r|\s)*/g,"");
- }
- new_str[ns++] = tmp2;
- }
- from = tokenizer.lastIndex;
-
- if (tmp[0] == "\"" && !in_multiline_comment && !in_singleline_comment) {
- tmp2 = lc.match(/(\\)*$/);
- if (!in_string || !tmp2 || (tmp2[0].length % 2) == 0) { // start of string with ", or unescaped " character found to end string
- in_string = !in_string;
- }
- from--; // include " character in next catch
- rc = json.substring(from);
- }
- else if (tmp[0] == "/*" && !in_string && !in_multiline_comment && !in_singleline_comment) {
- in_multiline_comment = true;
- }
- else if (tmp[0] == "*/" && !in_string && in_multiline_comment && !in_singleline_comment) {
- in_multiline_comment = false;
- }
- else if (tmp[0] == "//" && !in_string && !in_multiline_comment && !in_singleline_comment) {
- in_singleline_comment = true;
- }
- else if ((tmp[0] == "\n" || tmp[0] == "\r") && !in_string && !in_multiline_comment && in_singleline_comment) {
- in_singleline_comment = false;
- }
- else if (!in_multiline_comment && !in_singleline_comment && !(/\n|\r|\s/.test(tmp[0]))) {
- new_str[ns++] = tmp[0];
- }
- }
- new_str[ns++] = rc;
- return new_str.join("");
- };
-})(this);
diff --git a/src/static/js/admin/plugins.js b/src/static/js/admin/plugins.js
deleted file mode 100644
index e51e1102d..000000000
--- a/src/static/js/admin/plugins.js
+++ /dev/null
@@ -1,273 +0,0 @@
-'use strict';
-
-/* global socketio */
-
-$(document).ready(() => {
- const socket = socketio.connect('..', '/pluginfw/installer');
- socket.on('disconnect', (reason) => {
- // The socket.io client will automatically try to reconnect for all reasons other than "io
- // server disconnect".
- if (reason === 'io server disconnect') socket.connect();
- });
-
- const search = (searchTerm, limit) => {
- if (search.searchTerm !== searchTerm) {
- search.offset = 0;
- search.results = [];
- search.end = false;
- }
- limit = limit ? limit : search.limit;
- search.searchTerm = searchTerm;
- socket.emit('search', {
- searchTerm,
- offset: search.offset,
- limit,
- sortBy: search.sortBy,
- sortDir: search.sortDir,
- });
- search.offset += limit;
-
- $('#search-progress').show();
- search.messages.show('fetching');
- search.searching = true;
- };
- search.searching = false;
- search.offset = 0;
- search.limit = 999;
- search.results = [];
- search.sortBy = 'name';
- search.sortDir = /* DESC?*/true;
- search.end = true;// have we received all results already?
- search.messages = {
- show: (msg) => {
- // $('.search-results .messages').show()
- $(`.search-results .messages .${msg}`).show();
- $(`.search-results .messages .${msg} *`).show();
- },
- hide: (msg) => {
- $('.search-results .messages').hide();
- $(`.search-results .messages .${msg}`).hide();
- $(`.search-results .messages .${msg} *`).hide();
- },
- };
-
- const installed = {
- progress: {
- show: (plugin, msg) => {
- $(`.installed-results .${plugin} .progress`).show();
- $(`.installed-results .${plugin} .progress .message`).text(msg);
- if ($(window).scrollTop() > $(`.${plugin}`).offset().top) {
- $(window).scrollTop($(`.${plugin}`).offset().top - 100);
- }
- },
- hide: (plugin) => {
- $(`.installed-results .${plugin} .progress`).hide();
- $(`.installed-results .${plugin} .progress .message`).text('');
- },
- },
- messages: {
- show: (msg) => {
- $('.installed-results .messages').show();
- $(`.installed-results .messages .${msg}`).show();
- },
- hide: (msg) => {
- $('.installed-results .messages').hide();
- $(`.installed-results .messages .${msg}`).hide();
- },
- },
- list: [],
- };
-
- const displayPluginList = (plugins, container, template) => {
- plugins.forEach((plugin) => {
- const row = template.clone();
-
- for (const attr in plugin) {
- if (attr === 'name') { // Hack to rewrite URLS into name
- const link = $('')
- .attr('href', `https://npmjs.org/package/${plugin.name}`)
- .attr('plugin', 'Plugin details')
- .attr('rel', 'noopener noreferrer')
- .attr('target', '_blank')
- .text(plugin.name.substr(3));
- row.find('.name').append(link);
- } else {
- row.find(`.${attr}`).text(plugin[attr]);
- }
- }
- row.find('.version').text(plugin.version);
- row.addClass(plugin.name);
- row.data('plugin', plugin.name);
- container.append(row);
- });
- updateHandlers();
- };
-
- const sortPluginList = (plugins, property, /* ASC?*/dir) => plugins.sort((a, b) => {
- if (a[property] < b[property]) return dir ? -1 : 1;
- if (a[property] > b[property]) return dir ? 1 : -1;
- // a must be equal to b
- return 0;
- });
-
- const updateHandlers = () => {
- // Search
- $('#search-query').off('keyup').on('keyup', () => {
- search($('#search-query').val());
- });
-
- // Prevent form submit
- $('#search-query').parent().on('submit', () => false);
-
- // update & install
- $('.do-install, .do-update').off('click').on('click', function (e) {
- const $row = $(e.target).closest('tr');
- const plugin = $row.data('plugin');
- if ($(this).hasClass('do-install')) {
- $row.remove().appendTo('#installed-plugins');
- installed.progress.show(plugin, 'Installing');
- } else {
- installed.progress.show(plugin, 'Updating');
- }
- socket.emit('install', plugin);
- installed.messages.hide('nothing-installed');
- });
-
- // uninstall
- $('.do-uninstall').off('click').on('click', (e) => {
- const $row = $(e.target).closest('tr');
- const pluginName = $row.data('plugin');
- socket.emit('uninstall', pluginName);
- installed.progress.show(pluginName, 'Uninstalling');
- installed.list = installed.list.filter((plugin) => plugin.name !== pluginName);
- });
-
- // Sort
- $('.sort.up').off('click').on('click', function () {
- search.sortBy = $(this).attr('data-label').toLowerCase();
- search.sortDir = false;
- search.offset = 0;
- search(search.searchTerm, search.results.length);
- search.results = [];
- });
- $('.sort.down, .sort.none').off('click').on('click', function () {
- search.sortBy = $(this).attr('data-label').toLowerCase();
- search.sortDir = true;
- search.offset = 0;
- search(search.searchTerm, search.results.length);
- search.results = [];
- });
- };
-
- socket.on('results:search', (data) => {
- if (!data.results.length) search.end = true;
- if (data.query.offset === 0) search.results = [];
- search.messages.hide('nothing-found');
- search.messages.hide('fetching');
- $('#search-query').prop('disabled', false);
-
- console.log('got search results', data);
-
- // add to results
- search.results = search.results.concat(data.results);
-
- // Update sorting head
- $('.sort')
- .removeClass('up down')
- .addClass('none');
- $(`.search-results thead th[data-label=${data.query.sortBy}]`)
- .removeClass('none')
- .addClass(data.query.sortDir ? 'up' : 'down');
-
- // re-render search results
- const searchWidget = $('.search-results');
- searchWidget.find('.results *').remove();
- if (search.results.length > 0) {
- displayPluginList(
- search.results, searchWidget.find('.results'), searchWidget.find('.template tr'));
- } else {
- search.messages.show('nothing-found');
- }
- search.messages.hide('fetching');
- $('#search-progress').hide();
- search.searching = false;
- });
-
- socket.on('results:installed', (data) => {
- installed.messages.hide('fetching');
- installed.messages.hide('nothing-installed');
-
- installed.list = data.installed;
- sortPluginList(installed.list, 'name', /* ASC?*/true);
-
- // filter out epl
- installed.list = installed.list.filter((plugin) => plugin.name !== 'ep_etherpad-lite');
-
- // remove all installed plugins (leave plugins that are still being installed)
- installed.list.forEach((plugin) => {
- $(`#installed-plugins .${plugin.name}`).remove();
- });
-
- if (installed.list.length > 0) {
- displayPluginList(installed.list, $('#installed-plugins'), $('#installed-plugin-template'));
- socket.emit('checkUpdates');
- } else {
- installed.messages.show('nothing-installed');
- }
- });
-
- socket.on('results:updatable', (data) => {
- data.updatable.forEach((pluginName) => {
- const actions = $(`#installed-plugins > tr.${pluginName} .actions`);
- actions.find('.do-update').remove();
- actions.append(
- $('').addClass('do-update').attr('type', 'button').attr('value', 'Update'));
- });
- updateHandlers();
- });
-
- socket.on('finished:install', (data) => {
- if (data.error) {
- if (data.code === 'EPEERINVALID') {
- alert("This plugin requires that you update Etherpad so it can operate in it's true glory");
- }
- alert(`An error occurred while installing ${data.plugin} \n${data.error}`);
- $(`#installed-plugins .${data.plugin}`).remove();
- }
-
- socket.emit('getInstalled');
-
- // update search results
- search.offset = 0;
- search(search.searchTerm, search.results.length);
- search.results = [];
- });
-
- socket.on('finished:uninstall', (data) => {
- if (data.error) {
- alert(`An error occurred while uninstalling the ${data.plugin} \n${data.error}`);
- }
-
- // remove plugin from installed list
- $(`#installed-plugins .${data.plugin}`).remove();
-
- socket.emit('getInstalled');
-
- // update search results
- search.offset = 0;
- search(search.searchTerm, search.results.length);
- search.results = [];
- });
-
- socket.on('connect', () => {
- updateHandlers();
- socket.emit('getInstalled');
- search.searchTerm = null;
- search($('#search-query').val());
- });
-
- // check for updates every 5mins
- setInterval(() => {
- socket.emit('checkUpdates');
- }, 1000 * 60 * 5);
-});
diff --git a/src/static/js/admin/settings.js b/src/static/js/admin/settings.js
deleted file mode 100644
index 4123546e1..000000000
--- a/src/static/js/admin/settings.js
+++ /dev/null
@@ -1,69 +0,0 @@
-'use strict';
-
-$(document).ready(() => {
- const socket = window.socketio.connect('..', '/settings');
-
- socket.on('connect', () => {
- socket.emit('load');
- });
-
- socket.on('disconnect', (reason) => {
- // The socket.io client will automatically try to reconnect for all reasons other than "io
- // server disconnect".
- if (reason === 'io server disconnect') socket.connect();
- });
-
- socket.on('settings', (settings) => {
- /* Check whether the settings.json is authorized to be viewed */
- if (settings.results === 'NOT_ALLOWED') {
- $('.innerwrapper').hide();
- $('.innerwrapper-err').show();
- $('.err-message').html('Settings json is not authorized to be viewed in Admin page!!');
- return;
- }
-
- /* Check to make sure the JSON is clean before proceeding */
- if (isJSONClean(settings.results)) {
- $('.settings').append(settings.results);
- $('.settings').trigger('focus');
- $('.settings').autosize();
- } else {
- alert('Invalid JSON');
- }
- });
-
- /* When the admin clicks save Settings check the JSON then send the JSON back to the server */
- $('#saveSettings').on('click', () => {
- const editedSettings = $('.settings').val();
- if (isJSONClean(editedSettings)) {
- // JSON is clean so emit it to the server
- socket.emit('saveSettings', $('.settings').val());
- } else {
- alert('Invalid JSON');
- $('.settings').trigger('focus');
- }
- });
-
- /* Tell Etherpad Server to restart */
- $('#restartEtherpad').on('click', () => {
- socket.emit('restartServer');
- });
-
- socket.on('saveprogress', (progress) => {
- $('#response').show();
- $('#response').text(progress);
- $('#response').fadeOut('slow');
- });
-});
-
-
-const isJSONClean = (data) => {
- let cleanSettings = JSON.minify(data);
- // this is a bit naive. In theory some key/value might contain the sequences ',]' or ',}'
- cleanSettings = cleanSettings.replace(',]', ']').replace(',}', '}');
- try {
- return typeof JSON.parse(cleanSettings) === 'object';
- } catch (e) {
- return false; // the JSON failed to be parsed
- }
-};
diff --git a/src/templates/admin/index.html b/src/templates/admin/index.html
deleted file mode 100644
index d4478153a..000000000
--- a/src/templates/admin/index.html
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
-
-
-
-
-
-
diff --git a/src/templates/admin/plugins-info.html b/src/templates/admin/plugins-info.html
deleted file mode 100644
index 57e41f852..000000000
--- a/src/templates/admin/plugins-info.html
+++ /dev/null
@@ -1,47 +0,0 @@
-
-
-
-
-
-
-
-
-
-
diff --git a/src/templates/admin/plugins.html b/src/templates/admin/plugins.html
deleted file mode 100644
index 278304faf..000000000
--- a/src/templates/admin/plugins.html
+++ /dev/null
@@ -1,121 +0,0 @@
-
-
-
-
-
- Etherpad version-Version number: <%= epVersion %> -Latest available version: <%= latestVersion %> -Git sha: <%= gitCommit %> - -Installed plugins- <%- installedPlugins %> - -Installed parts- <%- installedParts %> - -Installed hooks-Server-side hooks- <%- installedServerHooks %> - -Client-side hooks- <%- installedClientHooks %> - -
-
- <% if (errors.length) { %>
-
-
-
-
diff --git a/src/templates/admin/settings.html b/src/templates/admin/settings.html
deleted file mode 100644
index ffa4172f7..000000000
--- a/src/templates/admin/settings.html
+++ /dev/null
@@ -1,58 +0,0 @@
-
-
-
-
- <% errors.forEach(function (item) { %>
-
- <% } %>
-
-
-
- <%= item.toString() %>
- <% }) %>
-
-
- Installed plugins-
-
-
-
-
- Available plugins- - -
-
- <% if (errors.length) { %>
-
-
-
-
diff --git a/src/templates/javascript.html b/src/templates/javascript.html
index 42482f69d..c501af65c 100644
--- a/src/templates/javascript.html
+++ b/src/templates/javascript.html
@@ -34,24 +34,16 @@
- <% errors.forEach(function (item) { %>
-
- <% } %>
-
-
-
-
- <%= item.toString() %>
- <% }) %>
-
-
-
- Current configuration- - - - - - Example production settings template - Example development settings template -
-
-
-
- require-kernel.js |
plugins.js |
Apache-2.0-only |
- plugins.js |
minify.json.js |
Expat |
- minify.json.js |
settings.js |
Apache-2.0-only |
- settings.js |
jquery.autosize.js |
Expat |
- jquery.autosize.js |
|
---|