Showing preview only (4,300K chars total). Download the full file or copy to clipboard to get everything.
Repository: ether/etherpad-lite
Branch: develop
Commit: 29ad3d93832d
Files: 749
Total size: 4.0 MB
Directory structure:
gitextract_fnv49fja/
├── .dockerignore
├── .editorconfig
├── .gitattributes
├── .github/
│ ├── FUNDING.yml
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.md
│ │ ├── feature_request.md
│ │ ├── plugin-request-template.md
│ │ ├── security-issue.md
│ │ └── security.md
│ ├── PULL_REQUEST_TEMPLATE.md
│ ├── dependabot.yml
│ └── workflows/
│ ├── backend-tests.yml
│ ├── build-and-deploy-docs.yml
│ ├── codeql-analysis.yml
│ ├── dependency-review.yml
│ ├── docker.yml
│ ├── frontend-admin-tests.yml
│ ├── frontend-tests.yml
│ ├── handleRelease.yml
│ ├── load-test.yml
│ ├── perform-type-check.yml
│ ├── rate-limit.yml
│ ├── release.yml
│ ├── releaseEtherpad.yml
│ ├── stale.yml
│ └── upgrade-from-latest-release.yml
├── .gitignore
├── .lgtm.yml
├── .pr_agent.toml
├── .travis.yml
├── AGENTS.MD
├── CHANGELOG.md
├── CONTRIBUTING.md
├── Dockerfile
├── LICENSE
├── README.md
├── SECURITY.md
├── admin/
│ ├── .eslintrc.cjs
│ ├── .gitignore
│ ├── README.md
│ ├── index.html
│ ├── package.json
│ ├── public/
│ │ └── ep_admin_pads/
│ │ ├── ar.json
│ │ ├── bn.json
│ │ ├── ca.json
│ │ ├── cs.json
│ │ ├── cy.json
│ │ ├── da.json
│ │ ├── de.json
│ │ ├── diq.json
│ │ ├── dsb.json
│ │ ├── el.json
│ │ ├── en.json
│ │ ├── eu.json
│ │ ├── ff.json
│ │ ├── fi.json
│ │ ├── fr.json
│ │ ├── gl.json
│ │ ├── he.json
│ │ ├── hsb.json
│ │ ├── hu.json
│ │ ├── ia.json
│ │ ├── it.json
│ │ ├── kn.json
│ │ ├── ko.json
│ │ ├── krc.json
│ │ ├── lb.json
│ │ ├── lt.json
│ │ ├── mk.json
│ │ ├── my.json
│ │ ├── nb.json
│ │ ├── nl.json
│ │ ├── oc.json
│ │ ├── pms.json
│ │ ├── pt-br.json
│ │ ├── pt.json
│ │ ├── qqq.json
│ │ ├── ru.json
│ │ ├── sc.json
│ │ ├── sdc.json
│ │ ├── sk.json
│ │ ├── skr-arab.json
│ │ ├── sl.json
│ │ ├── smn.json
│ │ ├── sms.json
│ │ ├── sq.json
│ │ ├── sv.json
│ │ ├── sw.json
│ │ ├── th.json
│ │ ├── tl.json
│ │ ├── tr.json
│ │ ├── uk.json
│ │ ├── zh-hans.json
│ │ └── zh-hant.json
│ ├── src/
│ │ ├── App.css
│ │ ├── App.tsx
│ │ ├── components/
│ │ │ ├── IconButton.tsx
│ │ │ ├── SearchField.tsx
│ │ │ └── ShoutType.ts
│ │ ├── index.css
│ │ ├── localization/
│ │ │ └── i18n.ts
│ │ ├── main.tsx
│ │ ├── pages/
│ │ │ ├── HelpPage.tsx
│ │ │ ├── HomePage.tsx
│ │ │ ├── LoginScreen.tsx
│ │ │ ├── PadPage.tsx
│ │ │ ├── Plugin.ts
│ │ │ ├── SettingsPage.tsx
│ │ │ └── ShoutPage.tsx
│ │ ├── store/
│ │ │ └── store.ts
│ │ ├── utils/
│ │ │ ├── AnimationFrameHook.ts
│ │ │ ├── LoadingScreen.tsx
│ │ │ ├── PadSearch.ts
│ │ │ ├── Toast.tsx
│ │ │ ├── sorting.ts
│ │ │ ├── useDebounce.ts
│ │ │ └── utils.ts
│ │ └── vite-env.d.ts
│ ├── tsconfig.json
│ ├── tsconfig.node.json
│ └── vite.config.ts
├── best_practices.md
├── bin/
│ ├── buildDebian.sh
│ ├── buildForWindows.sh
│ ├── checkAllPads.ts
│ ├── checkPad.ts
│ ├── cleanRun.sh
│ ├── commonPlugins.ts
│ ├── convertSettings.json.template
│ ├── createRelease.sh
│ ├── createUserSession.ts
│ ├── deb-src/
│ │ ├── DEBIAN/
│ │ │ ├── control
│ │ │ ├── postinst
│ │ │ ├── preinst
│ │ │ └── prerm
│ │ └── sysroot/
│ │ └── etc/
│ │ └── init/
│ │ └── etherpad.conf
│ ├── debugRun.sh
│ ├── deleteAllGroupSessions.ts
│ ├── deletePad.ts
│ ├── extractPadData.ts
│ ├── fastRun.sh
│ ├── functions.sh
│ ├── generateReleaseNotes.ts
│ ├── importSqlFile.ts
│ ├── installDeps.sh
│ ├── installLocalPlugins.sh
│ ├── installOnWindows.bat
│ ├── make_docs.ts
│ ├── migrateDB.ts
│ ├── migrateDirtyDBtoRealDB.ts
│ ├── nsis/
│ │ ├── README.md
│ │ └── etherpad.nsi
│ ├── package.json
│ ├── plugins/
│ │ ├── README.md
│ │ ├── checkPlugin.ts
│ │ ├── getCorePlugins.sh
│ │ ├── lib/
│ │ │ ├── CONTRIBUTING.md
│ │ │ ├── LICENSE
│ │ │ ├── README.md
│ │ │ ├── backend-tests.yml
│ │ │ ├── dependabot.yml
│ │ │ ├── eslintrc.cjs
│ │ │ ├── frontend-tests.yml
│ │ │ ├── gitignore
│ │ │ ├── npmpublish.yml
│ │ │ └── test-and-release.yml
│ │ ├── listOfficialPlugins
│ │ ├── reTestAllPlugins.sh
│ │ ├── stalePlugins.ts
│ │ ├── updateAllPluginsScript.sh
│ │ └── updateCorePlugins.sh
│ ├── plugins.ts
│ ├── push-after-release.sh
│ ├── rebuildPad.ts
│ ├── release.ts
│ ├── repairPad.ts
│ ├── run.sh
│ ├── safeRun.sh
│ ├── tsconfig.json
│ └── updatePlugins.sh
├── doc/
│ ├── .gitignore
│ ├── .vitepress/
│ │ ├── config.mts
│ │ └── theme/
│ │ ├── components/
│ │ │ └── SvgImage.vue
│ │ ├── index.ts
│ │ └── styles/
│ │ └── vars.css
│ ├── api/
│ │ ├── api.adoc
│ │ ├── changeset_library.adoc
│ │ ├── changeset_library.md
│ │ ├── editbar.adoc
│ │ ├── editbar.md
│ │ ├── editorInfo.adoc
│ │ ├── editorInfo.md
│ │ ├── embed_parameters.adoc
│ │ ├── embed_parameters.md
│ │ ├── hooks_client-side.adoc
│ │ ├── hooks_client-side.md
│ │ ├── hooks_overview.adoc
│ │ ├── hooks_overview.md
│ │ ├── hooks_server-side.adoc
│ │ ├── hooks_server-side.md
│ │ ├── http_api.adoc
│ │ ├── http_api.md
│ │ ├── index.md
│ │ ├── pluginfw.adoc
│ │ ├── pluginfw.md
│ │ ├── toolbar.adoc
│ │ └── toolbar.md
│ ├── assets/
│ │ └── style.css
│ ├── cli.md
│ ├── cookies.adoc
│ ├── cookies.md
│ ├── database.adoc
│ ├── demo.md
│ ├── docker.adoc
│ ├── docker.md
│ ├── documentation.adoc
│ ├── documentation.md
│ ├── index.adoc
│ ├── index.md
│ ├── localization.adoc
│ ├── localization.md
│ ├── package.json
│ ├── plugins.adoc
│ ├── plugins.md
│ ├── public/
│ │ └── easysync/
│ │ ├── README.md
│ │ ├── easysync-full-description.tex
│ │ ├── easysync-notes.tex
│ │ └── easysync-notes.txt
│ ├── skins.adoc
│ ├── skins.md
│ ├── stats.adoc
│ └── stats.md
├── docker-compose.dev.yml
├── docker-compose.yml
├── local_plugins/
│ └── .gitignore
├── package.json
├── pnpm-workspace.yaml
├── settings.json.docker
├── settings.json.template
├── src/
│ ├── .eslintrc.cjs
│ ├── README.md
│ ├── ep.json
│ ├── locales/
│ │ ├── af.json
│ │ ├── ar.json
│ │ ├── ast.json
│ │ ├── awa.json
│ │ ├── az.json
│ │ ├── azb.json
│ │ ├── bcc.json
│ │ ├── be-tarask.json
│ │ ├── bg.json
│ │ ├── bgn.json
│ │ ├── bn.json
│ │ ├── br.json
│ │ ├── bs.json
│ │ ├── ca.json
│ │ ├── ce.json
│ │ ├── cs.json
│ │ ├── da.json
│ │ ├── de.json
│ │ ├── diq.json
│ │ ├── dsb.json
│ │ ├── dty.json
│ │ ├── el.json
│ │ ├── en-gb.json
│ │ ├── en.json
│ │ ├── eo.json
│ │ ├── es.json
│ │ ├── et.json
│ │ ├── eu.json
│ │ ├── fa.json
│ │ ├── ff.json
│ │ ├── fi.json
│ │ ├── fo.json
│ │ ├── fr.json
│ │ ├── fy.json
│ │ ├── ga.json
│ │ ├── gl.json
│ │ ├── got.json
│ │ ├── gu.json
│ │ ├── he.json
│ │ ├── hi.json
│ │ ├── hr.json
│ │ ├── hrx.json
│ │ ├── hsb.json
│ │ ├── hu.json
│ │ ├── hy.json
│ │ ├── ia.json
│ │ ├── id.json
│ │ ├── is.json
│ │ ├── it.json
│ │ ├── ja.json
│ │ ├── kab.json
│ │ ├── km.json
│ │ ├── kn.json
│ │ ├── ko.json
│ │ ├── krc.json
│ │ ├── ksh.json
│ │ ├── ku-latn.json
│ │ ├── lb.json
│ │ ├── lki.json
│ │ ├── lrc.json
│ │ ├── lt.json
│ │ ├── lv.json
│ │ ├── map-bms.json
│ │ ├── mg.json
│ │ ├── mk.json
│ │ ├── ml.json
│ │ ├── mn.json
│ │ ├── mnw.json
│ │ ├── mr.json
│ │ ├── ms.json
│ │ ├── my.json
│ │ ├── nah.json
│ │ ├── nap.json
│ │ ├── nb.json
│ │ ├── nds.json
│ │ ├── ne.json
│ │ ├── nl.json
│ │ ├── nn.json
│ │ ├── oc.json
│ │ ├── olo.json
│ │ ├── os.json
│ │ ├── pa.json
│ │ ├── pl.json
│ │ ├── pms.json
│ │ ├── ps.json
│ │ ├── pt-br.json
│ │ ├── pt.json
│ │ ├── qqq.json
│ │ ├── ro.json
│ │ ├── ru.json
│ │ ├── sc.json
│ │ ├── sco.json
│ │ ├── sd.json
│ │ ├── sh-latn.json
│ │ ├── shn.json
│ │ ├── sk.json
│ │ ├── skr-arab.json
│ │ ├── sl.json
│ │ ├── sms.json
│ │ ├── sq.json
│ │ ├── sr-ec.json
│ │ ├── sr-el.json
│ │ ├── sro.json
│ │ ├── sv.json
│ │ ├── sw.json
│ │ ├── ta.json
│ │ ├── tcy.json
│ │ ├── te.json
│ │ ├── th.json
│ │ ├── tr.json
│ │ ├── uk.json
│ │ ├── vec.json
│ │ ├── vi.json
│ │ ├── zh-hans.json
│ │ └── zh-hant.json
│ ├── node/
│ │ ├── README.md
│ │ ├── db/
│ │ │ ├── API.ts
│ │ │ ├── AuthorManager.ts
│ │ │ ├── DB.ts
│ │ │ ├── GroupManager.ts
│ │ │ ├── Pad.ts
│ │ │ ├── PadManager.ts
│ │ │ ├── ReadOnlyManager.ts
│ │ │ ├── SecurityManager.ts
│ │ │ ├── SessionManager.ts
│ │ │ └── SessionStore.ts
│ │ ├── eejs/
│ │ │ └── index.ts
│ │ ├── handler/
│ │ │ ├── APIHandler.ts
│ │ │ ├── APIKeyHandler.ts
│ │ │ ├── ExportHandler.ts
│ │ │ ├── ImportHandler.ts
│ │ │ ├── PadMessageHandler.ts
│ │ │ ├── RestAPI.ts
│ │ │ └── SocketIORouter.ts
│ │ ├── hooks/
│ │ │ ├── express/
│ │ │ │ ├── admin.ts
│ │ │ │ ├── adminplugins.ts
│ │ │ │ ├── adminsettings.ts
│ │ │ │ ├── apicalls.ts
│ │ │ │ ├── errorhandling.ts
│ │ │ │ ├── importexport.ts
│ │ │ │ ├── openapi.ts
│ │ │ │ ├── padurlsanitize.ts
│ │ │ │ ├── pwa.ts
│ │ │ │ ├── socketio.ts
│ │ │ │ ├── specialpages.ts
│ │ │ │ ├── static.ts
│ │ │ │ ├── tokenTransfer.ts
│ │ │ │ └── webaccess.ts
│ │ │ ├── express.ts
│ │ │ └── i18n.ts
│ │ ├── metrics.ts
│ │ ├── padaccess.ts
│ │ ├── prometheus.ts
│ │ ├── security/
│ │ │ ├── OAuth2Provider.ts
│ │ │ ├── OAuth2User.ts
│ │ │ ├── OIDCAdapter.ts
│ │ │ ├── SecretRotator.ts
│ │ │ └── crypto.ts
│ │ ├── server.ts
│ │ ├── stats.ts
│ │ ├── types/
│ │ │ ├── APIHandlerType.ts
│ │ │ ├── ArgsExpressType.ts
│ │ │ ├── AsyncQueueTask.ts
│ │ │ ├── ChangeSet.ts
│ │ │ ├── DeriveModel.ts
│ │ │ ├── ErrorCaused.ts
│ │ │ ├── I18nPluginDefs.ts
│ │ │ ├── LegacyParams.ts
│ │ │ ├── MapType.ts
│ │ │ ├── PackageInfo.ts
│ │ │ ├── PadSearchQuery.ts
│ │ │ ├── PadType.ts
│ │ │ ├── PartType.ts
│ │ │ ├── Plugin.ts
│ │ │ ├── PromiseWithStd.ts
│ │ │ ├── QueryType.ts
│ │ │ ├── Revision.ts
│ │ │ ├── RunCMDOptions.ts
│ │ │ ├── SecretRotatorType.ts
│ │ │ ├── SettingsUser.ts
│ │ │ ├── SocketAcknowledge.ts
│ │ │ ├── SocketClientRequest.ts
│ │ │ ├── SocketModule.ts
│ │ │ ├── SwaggerUIResource.ts
│ │ │ ├── UserSettingsObject.ts
│ │ │ └── WebAccessTypes.ts
│ │ └── utils/
│ │ ├── Abiword.ts
│ │ ├── AbsolutePaths.ts
│ │ ├── Cleanup.ts
│ │ ├── Cli.ts
│ │ ├── ExportEtherpad.ts
│ │ ├── ExportHelper.ts
│ │ ├── ExportHtml.ts
│ │ ├── ExportTxt.ts
│ │ ├── ImportEtherpad.ts
│ │ ├── ImportHtml.ts
│ │ ├── LibreOffice.ts
│ │ ├── Minify.ts
│ │ ├── MinifyWorker.ts
│ │ ├── NodeVersion.ts
│ │ ├── Settings.ts
│ │ ├── SettingsTree.ts
│ │ ├── Stream.ts
│ │ ├── UpdateCheck.ts
│ │ ├── checkValidRev.ts
│ │ ├── customError.ts
│ │ ├── padDiff.ts
│ │ ├── path_exists.ts
│ │ ├── promises.ts
│ │ ├── randomstring.ts
│ │ ├── run_cmd.ts
│ │ ├── sanitizePathname.ts
│ │ ├── tar.json
│ │ └── toolbar.ts
│ ├── package.json
│ ├── playwright.config.ts
│ ├── static/
│ │ ├── css/
│ │ │ ├── admin.css
│ │ │ ├── iframe_editor.css
│ │ │ ├── lists_and_indents.css
│ │ │ ├── pad/
│ │ │ │ ├── chat.css
│ │ │ │ ├── fonts.css
│ │ │ │ ├── form.css
│ │ │ │ ├── gritter.css
│ │ │ │ ├── icons.css
│ │ │ │ ├── layout.css
│ │ │ │ ├── loadingbox.css
│ │ │ │ ├── normalize.css
│ │ │ │ ├── popup.css
│ │ │ │ ├── popup_connectivity.css
│ │ │ │ ├── popup_import_export.css
│ │ │ │ ├── popup_users.css
│ │ │ │ └── toolbar.css
│ │ │ ├── pad.css
│ │ │ └── timeslider.css
│ │ ├── empty.html
│ │ ├── font/
│ │ │ ├── Montserrat-Light.otf
│ │ │ ├── Montserrat-Regular.otf
│ │ │ ├── config.json
│ │ │ └── opendyslexic.otf
│ │ ├── js/
│ │ │ ├── AttributeManager.ts
│ │ │ ├── AttributeMap.ts
│ │ │ ├── AttributePool.ts
│ │ │ ├── Builder.ts
│ │ │ ├── Changeset.ts
│ │ │ ├── ChangesetUtils.ts
│ │ │ ├── ChatMessage.ts
│ │ │ ├── MergingOpAssembler.ts
│ │ │ ├── Op.ts
│ │ │ ├── OpAssembler.ts
│ │ │ ├── OpIter.ts
│ │ │ ├── SmartOpAssembler.ts
│ │ │ ├── StringAssembler.ts
│ │ │ ├── StringIterator.ts
│ │ │ ├── TextLinesMutator.ts
│ │ │ ├── ace.ts
│ │ │ ├── ace2_common.ts
│ │ │ ├── ace2_inner.ts
│ │ │ ├── attributes.ts
│ │ │ ├── basic_error_handler.ts
│ │ │ ├── broadcast.ts
│ │ │ ├── broadcast_revisions.ts
│ │ │ ├── broadcast_slider.ts
│ │ │ ├── caretPosition.ts
│ │ │ ├── changesettracker.ts
│ │ │ ├── chat.ts
│ │ │ ├── collab_client.ts
│ │ │ ├── colorutils.ts
│ │ │ ├── contentcollector.ts
│ │ │ ├── cssmanager.ts
│ │ │ ├── domline.ts
│ │ │ ├── index.ts
│ │ │ ├── l10n.ts
│ │ │ ├── linestylefilter.ts
│ │ │ ├── pad.ts
│ │ │ ├── pad_automatic_reconnect.ts
│ │ │ ├── pad_connectionstatus.ts
│ │ │ ├── pad_cookie.ts
│ │ │ ├── pad_editbar.ts
│ │ │ ├── pad_editor.ts
│ │ │ ├── pad_impexp.ts
│ │ │ ├── pad_modals.ts
│ │ │ ├── pad_savedrevs.ts
│ │ │ ├── pad_userlist.ts
│ │ │ ├── pad_utils.ts
│ │ │ ├── pluginfw/
│ │ │ │ ├── LinkInstaller.ts
│ │ │ │ ├── client_plugins.ts
│ │ │ │ ├── hooks.ts
│ │ │ │ ├── installer.ts
│ │ │ │ ├── plugin_defs.ts
│ │ │ │ ├── plugins.ts
│ │ │ │ ├── shared.ts
│ │ │ │ └── tsort.ts
│ │ │ ├── rjquery.ts
│ │ │ ├── scroll.ts
│ │ │ ├── security.ts
│ │ │ ├── skin_variants.ts
│ │ │ ├── skiplist.ts
│ │ │ ├── socketio.ts
│ │ │ ├── timeslider.ts
│ │ │ ├── types/
│ │ │ │ ├── AText.ts
│ │ │ │ ├── Attribute.ts
│ │ │ │ ├── ChangeSet.ts
│ │ │ │ ├── ChangeSetBuilder.ts
│ │ │ │ ├── PadRevision.ts
│ │ │ │ ├── RepModel.ts
│ │ │ │ └── SocketIOMessage.ts
│ │ │ ├── underscore.ts
│ │ │ ├── undomodule.ts
│ │ │ ├── vendors/
│ │ │ │ ├── browser.ts
│ │ │ │ ├── farbtastic.ts
│ │ │ │ ├── gritter.ts
│ │ │ │ ├── html10n.ts
│ │ │ │ ├── jquery.ts
│ │ │ │ └── nice-select.ts
│ │ │ └── welcome.ts
│ │ ├── robots.txt
│ │ ├── skins/
│ │ │ ├── colibris/
│ │ │ │ ├── index.css
│ │ │ │ ├── index.js
│ │ │ │ ├── pad.css
│ │ │ │ ├── pad.js
│ │ │ │ ├── src/
│ │ │ │ │ ├── components/
│ │ │ │ │ │ ├── buttons.css
│ │ │ │ │ │ ├── chat.css
│ │ │ │ │ │ ├── form.css
│ │ │ │ │ │ ├── gritter.css
│ │ │ │ │ │ ├── import-export.css
│ │ │ │ │ │ ├── popup.css
│ │ │ │ │ │ ├── scrollbars.css
│ │ │ │ │ │ ├── sidediv.css
│ │ │ │ │ │ ├── table-of-content.css
│ │ │ │ │ │ ├── toolbar.css
│ │ │ │ │ │ └── users.css
│ │ │ │ │ ├── general.css
│ │ │ │ │ ├── layout.css
│ │ │ │ │ ├── pad-editor.css
│ │ │ │ │ ├── pad-variants.css
│ │ │ │ │ └── plugins/
│ │ │ │ │ ├── author_hover.css
│ │ │ │ │ ├── brightcolorpicker.css
│ │ │ │ │ ├── comments.css
│ │ │ │ │ ├── font_color.css
│ │ │ │ │ ├── set_title_on_pad.css
│ │ │ │ │ └── tables2.css
│ │ │ │ ├── timeslider.css
│ │ │ │ └── timeslider.js
│ │ │ └── no-skin/
│ │ │ ├── index.css
│ │ │ ├── index.js
│ │ │ ├── pad.css
│ │ │ ├── pad.js
│ │ │ ├── timeslider.css
│ │ │ └── timeslider.js
│ │ └── tests.html
│ ├── templates/
│ │ ├── export_html.html
│ │ ├── index.html
│ │ ├── indexBootstrap.js
│ │ ├── javascript.html
│ │ ├── pad.html
│ │ ├── padBootstrap.js
│ │ ├── padViteBootstrap.js
│ │ ├── timeSliderBootstrap.js
│ │ └── timeslider.html
│ ├── tests/
│ │ ├── README.md
│ │ ├── backend/
│ │ │ ├── common.ts
│ │ │ ├── fuzzImportTest.ts
│ │ │ └── specs/
│ │ │ ├── ExportEtherpad.ts
│ │ │ ├── ImportEtherpad.ts
│ │ │ ├── Pad.ts
│ │ │ ├── SecretRotator.ts
│ │ │ ├── SessionStore.ts
│ │ │ ├── Stream.ts
│ │ │ ├── api/
│ │ │ │ ├── api.ts
│ │ │ │ ├── characterEncoding.ts
│ │ │ │ ├── chat.ts
│ │ │ │ ├── emojis.html
│ │ │ │ ├── fuzzImportTest.ts
│ │ │ │ ├── importexport.ts
│ │ │ │ ├── importexportGetPost.ts
│ │ │ │ ├── instance.ts
│ │ │ │ ├── pad.ts
│ │ │ │ ├── restoreRevision.ts
│ │ │ │ ├── sessionsAndGroups.ts
│ │ │ │ ├── test.doc
│ │ │ │ ├── test.docx
│ │ │ │ ├── test.etherpad
│ │ │ │ ├── test.odt
│ │ │ │ └── test.txt
│ │ │ ├── chat.ts
│ │ │ ├── contentcollector.ts
│ │ │ ├── crypto.ts
│ │ │ ├── export.ts
│ │ │ ├── favicon.ts
│ │ │ ├── health.ts
│ │ │ ├── hooks.ts
│ │ │ ├── lowerCasePadIds.ts
│ │ │ ├── messages.ts
│ │ │ ├── pads-with-spaces.ts
│ │ │ ├── regression-db.ts
│ │ │ ├── settings.json
│ │ │ ├── settings.ts
│ │ │ ├── socketio.ts
│ │ │ ├── specialpages.ts
│ │ │ └── webaccess.ts
│ │ ├── backend-new/
│ │ │ ├── easysync-helper.ts
│ │ │ └── specs/
│ │ │ ├── AttributeMap.ts
│ │ │ ├── StringIteratorTest.ts
│ │ │ ├── admin_utils.ts
│ │ │ ├── attributes.ts
│ │ │ ├── easysync-assembler.ts
│ │ │ ├── easysync-compose.ts
│ │ │ ├── easysync-inverseRandom.ts
│ │ │ ├── easysync-mutations.ts
│ │ │ ├── easysync-other.test.ts
│ │ │ ├── easysync-subAttribution.ts
│ │ │ ├── pad_utils.ts
│ │ │ ├── path_exists.ts
│ │ │ ├── promises.ts
│ │ │ ├── sanitizePathname.ts
│ │ │ └── skiplist.ts
│ │ ├── container/
│ │ │ ├── loadSettings.js
│ │ │ └── specs/
│ │ │ └── api/
│ │ │ └── pad.js
│ │ ├── frontend/
│ │ │ ├── cypress/
│ │ │ │ ├── .gitignore
│ │ │ │ ├── README.md
│ │ │ │ ├── cypress.config.js
│ │ │ │ └── integration/
│ │ │ │ └── test.js
│ │ │ ├── easysync-helper.js
│ │ │ ├── helper/
│ │ │ │ ├── methods.ts
│ │ │ │ ├── multipleUsers.ts
│ │ │ │ └── ui.ts
│ │ │ ├── helper.js
│ │ │ ├── index.html
│ │ │ ├── runner.css
│ │ │ ├── runner.js
│ │ │ ├── specs/
│ │ │ │ ├── authorship_of_editions.js
│ │ │ │ ├── chat_hooks.js
│ │ │ │ ├── chat_load_messages.js
│ │ │ │ ├── drag_and_drop.js
│ │ │ │ ├── easysync-follow.js
│ │ │ │ ├── helper.js
│ │ │ │ ├── importexport.js
│ │ │ │ ├── importindents.js
│ │ │ │ ├── multiple_authors_clear_authorship_colors.js
│ │ │ │ ├── pad_modal.js
│ │ │ │ ├── responsiveness.js
│ │ │ │ ├── scrollTo.js
│ │ │ │ ├── select_formatting_buttons.js
│ │ │ │ ├── timeslider_labels.js
│ │ │ │ ├── timeslider_numeric_padID.js
│ │ │ │ ├── timeslider_revisions.js
│ │ │ │ └── xxauto_reconnect.js
│ │ │ └── travis/
│ │ │ ├── .gitignore
│ │ │ ├── adminrunner.sh
│ │ │ ├── remote_runner.js
│ │ │ ├── runner.sh
│ │ │ ├── runnerBackend.sh
│ │ │ └── runnerLoadTest.sh
│ │ ├── frontend-new/
│ │ │ ├── admin-spec/
│ │ │ │ ├── adminsettings.spec.ts
│ │ │ │ ├── admintroubleshooting.spec.ts
│ │ │ │ └── adminupdateplugins.spec.ts
│ │ │ ├── helper/
│ │ │ │ ├── adminhelper.ts
│ │ │ │ ├── padHelper.ts
│ │ │ │ ├── settingsHelper.ts
│ │ │ │ └── timeslider.ts
│ │ │ └── specs/
│ │ │ ├── alphabet.spec.ts
│ │ │ ├── bold.spec.ts
│ │ │ ├── change_user_color.spec.ts
│ │ │ ├── change_user_name.spec.ts
│ │ │ ├── chat.spec.ts
│ │ │ ├── clear_authorship_color.spec.ts
│ │ │ ├── collab_client.spec.ts
│ │ │ ├── delete.spec.ts
│ │ │ ├── editbar.spec.ts
│ │ │ ├── embed_value.spec.ts
│ │ │ ├── enter.spec.ts
│ │ │ ├── font_type.spec.ts
│ │ │ ├── indentation.spec.ts
│ │ │ ├── inner_height.spec.ts
│ │ │ ├── italic.spec.ts
│ │ │ ├── language.spec.ts
│ │ │ ├── ordered_list.spec.ts
│ │ │ ├── redo.spec.ts
│ │ │ ├── strikethrough.spec.ts
│ │ │ ├── timeslider.spec.ts
│ │ │ ├── timeslider_follow.spec.ts
│ │ │ ├── undo.spec.ts
│ │ │ ├── unordered_list.spec.ts
│ │ │ ├── urls_become_clickable.spec.ts
│ │ │ └── welcome.spec.test.ts
│ │ ├── ratelimit/
│ │ │ ├── Dockerfile.anotherip
│ │ │ ├── Dockerfile.nginx
│ │ │ ├── nginx.conf
│ │ │ ├── send_changesets.js
│ │ │ └── testlimits.sh
│ │ └── settings.json
│ ├── tsconfig.json
│ ├── vitest.config.ts
│ └── web.config
├── start.bat
├── ui/
│ ├── .gitignore
│ ├── consent.html
│ ├── login.html
│ ├── package.json
│ ├── pad.html
│ ├── src/
│ │ ├── consent.ts
│ │ ├── main.ts
│ │ ├── style.css
│ │ └── vite-env.d.ts
│ ├── tsconfig.json
│ └── vite.config.ts
└── var/
└── .gitignore
================================================
FILE CONTENTS
================================================
================================================
FILE: .dockerignore
================================================
*~
.dockerignore
.hg
Dockerfile
# Remove the git objects, logs, etc. to make final image smaller.
# Some files still need to be in the .git directory, because Etherpad at
# startup uses them to discover its version number.
.git/branches
.git/COMMIT_EDITMSG
.git/config
.git/description
.git/FETCH_HEAD
.git/hooks
.git/index
.git/info
.git/logs
.git/objects
.git/ORIG_HEAD
.git/packed-refs
.git/refs/remotes/
.git/rr-cache/
.gitignore
settings.json
src/node_modules
admin/node_modules
ui/node_modules
node_modules
================================================
FILE: .editorconfig
================================================
root = true
[*]
indent_style = space
indent_size = 2
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
end_of_line = lf
# editorconfig-tools is unable to ignore longs strings or urls
max_line_length = off
[CHANGELOG.md]
indent_size = 4
[*.bat]
end_of_line = crlf
================================================
FILE: .gitattributes
================================================
* text=auto eol=lf
================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms
github: ether
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
<!-- IMPORTANT: Please disable plugins prior to posting a bug report. If you have a problem with a plugin please post on the plugin repository. Thanks! -->
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Server (please complete the following information):**
- Etherpad version:
- OS: [e.g., Ubuntu 20.04]
- Node.js version (`node --version`):
- npm version (`npm --version`):
- Is the server free of plugins:
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]
**Additional context**
Add any other context about the problem here.
================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: Feature Request
assignees: ''
---
* * *
name: Feature request
about: Suggest an idea for this project
title: ''
labels: enhancement
assignees:
* * *
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when (...)
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
**Plugin?**
Might this feature be better suited to being a plugin? Usually features that can be plugins, should be.
================================================
FILE: .github/ISSUE_TEMPLATE/plugin-request-template.md
================================================
---
name: Plugin request template
about: Suggest a plugin for Etherpad
title: ''
labels: Plugin Request
assignees: JohnMcLear
---
* * *
name: Plugin request
about: Suggest a plugin for this project
title: ''
labels: plugin request
assignees:
* * *
**Is your plugin request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when (...)
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the plugin request here.
================================================
FILE: .github/ISSUE_TEMPLATE/security-issue.md
================================================
---
name: Security issue
about: Notify the Etherpad foundation of a Security issue
title: ''
labels: security
assignees: ''
---
Please email contact@etherpad.org with details of the security issue prior to posting here.
================================================
FILE: .github/ISSUE_TEMPLATE/security.md
================================================
* * *
name: Security notification
about: Disclose a security issue in Etherpad
title: ''
labels: security
assignees:
* * *
**Our Security disclosure process**
1. Please email contact@etherpad.org with detials of the exploit including steps to replicate.
1. Once confirmed we will provide a confirmation, patch and CVE details.
================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
<!--
1. If you haven't already, please read https://github.com/ether/etherpad-lite/blob/master/CONTRIBUTING.md#pull-requests .
2. Run all the tests, both front-end and back-end. (see https://github.com/ether/etherpad-lite/blob/master/CONTRIBUTING.md#testing)
3. Keep business logic and validation on the server-side.
4. Update documentation.
5. Write `fixes #XXXX` in your comment to auto-close an issue.
If you're making a big change, please explain what problem it solves:
- Explain the purpose of the change. When adding a way to do X, explain why it is important to be able to do X.
- Show the current vs desired behavior with screenshots/GIFs.
-->
================================================
FILE: .github/dependabot.yml
================================================
version: 2
updates:
# Maintain dependencies for GitHub Actions
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"
- package-ecosystem: "docker"
directory: "/"
schedule:
interval: "daily"
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "daily"
versioning-strategy: "increase"
open-pull-requests-limit: 30
groups:
dev-dependencies:
dependency-type: "development"
================================================
FILE: .github/workflows/backend-tests.yml
================================================
name: "Backend tests"
# any branch is useful for testing before a PR is submitted
on:
push:
paths-ignore:
- "doc/**"
pull_request:
paths-ignore:
- "doc/**"
permissions:
contents: read
jobs:
withoutpluginsLinux:
env:
PNPM_HOME: ~/.pnpm-store
# run on pushes to any branch
# run on PRs from external forks
if: |
(github.event_name != 'pull_request')
|| (github.event.pull_request.head.repo.id != github.event.pull_request.base.repo.id)
name: Linux without plugins
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
node: [">=20.0.0 <21.0.0", ">=22.0.0 <23.0.0", ">=24.0.0 <25.0.0"]
steps:
-
name: Checkout repository
uses: actions/checkout@v6
- uses: actions/cache@v5
name: Setup gnpm cache
if: always()
with:
path: |
${{ env.PNPM_HOME }}
~/.local/share/gnpm
/usr/local/bin/gnpm
/usr/local/bin/gnpm-0.0.12
key: ${{ runner.os }}-gnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-gnpm-store-
- name: Setup gnpm
uses: SamTV12345/gnpm-setup@main
with:
version: 0.0.12
-
name: Install libreoffice
uses: awalsh128/cache-apt-pkgs-action@v1.6.0
with:
packages: libreoffice libreoffice-pdfimport
version: 1.0
-
name: Install all dependencies and symlink for ep_etherpad-lite
run: gnpm i --frozen-lockfile --runtimeVersion="${{ matrix.node }}"
- name: Install admin ui
working-directory: admin
run: gnpm install --runtimeVersion="${{ matrix.node }}"
- name: Build admin ui
working-directory: admin
run: gnpm build --runtimeVersion="${{ matrix.node }}"
-
name: Run the backend tests
run: gnpm test --runtimeVersion="${{ matrix.node }}"
- name: Run the new vitest tests
working-directory: src
run: gnpm run test:vitest --runtimeVersion="${{ matrix.node }}"
withpluginsLinux:
env:
PNPM_HOME: ~/.pnpm-store
# run on pushes to any branch
# run on PRs from external forks
if: |
(github.event_name != 'pull_request')
|| (github.event.pull_request.head.repo.id != github.event.pull_request.base.repo.id)
name: Linux with Plugins
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
node: [">=20.0.0 <21.0.0", ">=22.0.0 <23.0.0", ">=24.0.0 <25.0.0"]
steps:
-
name: Checkout repository
uses: actions/checkout@v6
- uses: actions/cache@v5
name: Setup pnpm cache
if: always()
with:
path: |
${{ env.PNPM_HOME }}
~/.local/share/gnpm
/usr/local/bin/gnpm
/usr/local/bin/gnpm-0.0.12
key: ${{ runner.os }}-gnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-gnpm-store-
- name: Setup gnpm
uses: SamTV12345/gnpm-setup@main
with:
version: 0.0.12
-
name: Install libreoffice
uses: awalsh128/cache-apt-pkgs-action@v1.6.0
with:
packages: libreoffice libreoffice-pdfimport
version: 1.0
-
name: Install all dependencies and symlink for ep_etherpad-lite
run: gnpm install --frozen-lockfile --runtimeVersion="${{ matrix.node }}"
- name: Build admin ui
working-directory: admin
run: gnpm build --runtimeVersion="${{ matrix.node }}"
-
name: Install Etherpad plugins
run: >
gnpm install --workspace-root
ep_align
ep_author_hover
ep_cursortrace
ep_font_size
ep_hash_auth
ep_headings2
ep_markdown
ep_readonly_guest
ep_set_title_on_pad
ep_spellcheck
ep_subscript_and_superscript
ep_table_of_contents --runtimeVersion="${{ matrix.node }}"
-
name: Run the backend tests
run: gnpm test --runtimeVersion="${{ matrix.node }}"
- name: Run the new vitest tests
working-directory: src
run: gnpm run test:vitest --runtimeVersion="${{ matrix.node }}"
withoutpluginsWindows:
env:
PNPM_HOME: ~\\.pnpm-store
# run on pushes to any branch
# run on PRs from external forks
if: |
(github.event_name != 'pull_request')
|| (github.event.pull_request.head.repo.id != github.event.pull_request.base.repo.id)
strategy:
fail-fast: false
matrix:
node: [">=20.0.0 <21.0.0", ">=22.0.0 <23.0.0", ">=24.0.0 <25.0.0"]
name: Windows without plugins
runs-on: windows-latest
steps:
-
name: Checkout repository
uses: actions/checkout@v6
- uses: actions/cache@v5
name: Setup pnpm cache
if: always()
with:
path: |
${{ env.PNPM_HOME }}
C:\gnpm\
C:\Users\runneradmin\AppData\Roaming\gnpm\
key: ${{ runner.os }}-gnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-gnpm-store-
- name: Setup gnpm
uses: SamTV12345/gnpm-setup@main
with:
version: 0.0.12
-
name: Install all dependencies and symlink for ep_etherpad-lite
run: gnpm install --frozen-lockfile --runtimeVersion="${{ matrix.node }}"
- name: Build admin ui
working-directory: admin
run: gnpm build --runtimeVersion="${{ matrix.node }}"
-
name: Fix up the settings.json
run: |
powershell -Command "(gc settings.json.template) -replace '\"max\": 10', '\"max\": 10000' | Out-File -encoding ASCII settings.json.holder"
powershell -Command "(gc settings.json.holder) -replace '\"points\": 10', '\"points\": 1000' | Out-File -encoding ASCII settings.json"
-
name: Run the backend tests
working-directory: src
run: gnpm test --runtimeVersion="${{ matrix.node }}"
- name: Run the new vitest tests
working-directory: src
run: gnpm run test:vitest --runtimeVersion="${{ matrix.node }}"
withpluginsWindows:
env:
PNPM_HOME: ~\\.pnpm-store
# run on pushes to any branch
# run on PRs from external forks
if: |
(github.event_name != 'pull_request')
|| (github.event.pull_request.head.repo.id != github.event.pull_request.base.repo.id)
strategy:
fail-fast: false
matrix:
node: [">=20.0.0 <21.0.0", ">=22.0.0 <23.0.0", ">=24.0.0 <25.0.0"]
name: Windows with Plugins
runs-on: windows-latest
steps:
-
name: Checkout repository
uses: actions/checkout@v6
- uses: actions/cache@v5
name: Setup pnpm cache
if: always()
with:
path: |
${{ env.PNPM_HOME }}
C:\gnpm\
C:\Users\runneradmin\AppData\Roaming\gnpm\
key: ${{ runner.os }}-gnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-gnpm-store-
- name: Setup gnpm
uses: SamTV12345/gnpm-setup@main
with:
version: 0.0.12
- name: Install dependencies
run: gnpm install --runtimeVersion="${{ matrix.node }}"
- name: Build admin ui
working-directory: admin
run: gnpm build --runtimeVersion="${{ matrix.node }}"
-
name: Install Etherpad plugins
# The --legacy-peer-deps flag is required to work around a bug in npm
# v7: https://github.com/npm/cli/issues/2199
run: >
gnpm install --workspace-root
ep_align
ep_author_hover
ep_cursortrace
ep_font_size
ep_hash_auth
ep_headings2
ep_markdown
ep_readonly_guest
ep_set_title_on_pad
ep_spellcheck
ep_subscript_and_superscript
ep_table_of_contents --runtimeVersion="${{ matrix.node }}"
# Etherpad core dependencies must be installed after installing the
# plugin's dependencies, otherwise npm will try to hoist common
# dependencies by removing them from src/node_modules and installing them
# in the top-level node_modules. As of v6.14.10, npm's hoist logic appears
# to be buggy, because it sometimes removes dependencies from
# src/node_modules but fails to add them to the top-level node_modules.
# Even if npm correctly hoists the dependencies, the hoisting seems to
# confuse tools such as `npm outdated`, `npm update`, and some ESLint
# rules.
-
name: Install all dependencies and symlink for ep_etherpad-lite
run: gnpm install --frozen-lockfile --runtimeVersion="${{ matrix.node }}"
-
name: Fix up the settings.json
run: |
powershell -Command "(gc settings.json.template) -replace '\"max\": 10', '\"max\": 10000' | Out-File -encoding ASCII settings.json.holder"
powershell -Command "(gc settings.json.holder) -replace '\"points\": 10', '\"points\": 1000' | Out-File -encoding ASCII settings.json"
-
name: Run the backend tests
working-directory: src
run: gnpm test --runtimeVersion="${{ matrix.node }}"
- name: Run the new vitest tests
working-directory: src
run: gnpm run test:vitest --runtimeVersion="${{ matrix.node }}"
================================================
FILE: .github/workflows/build-and-deploy-docs.yml
================================================
# Workflow for deploying static content to GitHub Pages
name: Deploy Docs to GitHub Pages
on:
# Runs on pushes targeting the default branch
push:
branches: ["develop"]
paths:
- doc/** # Only run workflow when changes are made to the doc directory
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
contents: read
pages: write
id-token: write
packages: read
# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
concurrency:
group: "pages"
cancel-in-progress: false
jobs:
# Single deploy job since we're just deploying
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
- uses: actions/cache@v5
name: Setup gnpm cache
if: always()
with:
path: |
${{ env.STORE_PATH }}
~/.local/share/gnpm
/usr/local/bin/gnpm
/usr/local/bin/gnpm-0.0.12
key: ${{ runner.os }}-gnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-gnpm-store-
- name: Setup gnpm
uses: SamTV12345/gnpm-setup@main
with:
version: 0.0.12
- name: Setup Pages
uses: actions/configure-pages@v5
- name: Install dependencies
run: gnpm install
- name: Build app
working-directory: doc
run: gnpm run docs:build
env:
COMMIT_REF: ${{ github.sha }}
- name: Upload artifact
uses: actions/upload-pages-artifact@v4
with:
# Upload entire repository
path: './doc/.vitepress/dist'
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
================================================
FILE: .github/workflows/codeql-analysis.yml
================================================
name: "CodeQL"
on:
push:
branches: [develop, master]
pull_request:
# The branches below must be a subset of the branches above
branches: [develop]
paths-ignore:
- 'doc/**'
schedule:
- cron: '0 13 * * 1'
permissions:
contents: read
jobs:
analyze:
permissions:
actions: read # for github/codeql-action/init to get workflow details
contents: read # for actions/checkout to fetch code
security-events: write # for github/codeql-action/autobuild to send a status report
name: Analyze
runs-on: ubuntu-latest
steps:
-
name: Checkout repository
uses: actions/checkout@v6
with:
# We must fetch at least the immediate parents so that if this is
# a pull request then we can checkout the head.
fetch-depth: 2
# If this run was triggered by a pull request event, then checkout
# the head of the pull request instead of the merge commit.
-
run: git checkout HEAD^2
if: ${{ github.event_name == 'pull_request' }}
-
name: Initialize CodeQL
uses: github/codeql-action/init@v4
-
name: Autobuild
uses: github/codeql-action/autobuild@v4
-
name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v4
================================================
FILE: .github/workflows/dependency-review.yml
================================================
# Dependency Review Action
#
# This Action will scan dependency manifest files that change as part of a Pull Reqest, surfacing known-vulnerable versions of the packages declared or updated in the PR. Once installed, if the workflow run is marked as required, PRs introducing known-vulnerable packages will be blocked from merging.
#
# Source repository: https://github.com/actions/dependency-review-action
# Public documentation: https://docs.github.com/en/code-security/supply-chain-security/understanding-your-software-supply-chain/about-dependency-review#dependency-review-enforcement
name: 'Dependency Review'
on: [pull_request]
permissions:
contents: read
jobs:
dependency-review:
runs-on: ubuntu-latest
steps:
- name: 'Checkout Repository'
uses: actions/checkout@v6
- name: 'Dependency Review'
uses: actions/dependency-review-action@v4
================================================
FILE: .github/workflows/docker.yml
================================================
name: "Docker"
on:
pull_request:
paths-ignore:
- 'doc/**'
push:
branches:
- 'develop'
paths-ignore:
- 'doc/**'
tags:
- 'v?[0-9]+.[0-9]+.[0-9]+'
env:
TEST_TAG: etherpad/etherpad:test
permissions:
contents: read
jobs:
docker:
runs-on: ubuntu-latest
env:
PNPM_HOME: ~/.pnpm-store
steps:
-
name: Check out
uses: actions/checkout@v6
with:
path: etherpad
-
name: Set up QEMU
if: github.event_name == 'push'
uses: docker/setup-qemu-action@v4
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
-
name: Build and export to Docker
uses: docker/build-push-action@v7
with:
context: ./etherpad
target: production
load: true
tags: ${{ env.TEST_TAG }}
cache-from: type=gha
cache-to: type=gha,mode=max
- uses: actions/cache@v5
name: Setup gnpm cache
if: always()
with:
path: |
${{ env.PNPM_HOME }}
~/.local/share/gnpm
/usr/local/bin/gnpm
/usr/local/bin/gnpm-0.0.12
key: ${{ runner.os }}-gnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-gnpm-store-
- name: Setup gnpm
uses: SamTV12345/gnpm-setup@main
with:
version: 0.0.12
-
name: Test
working-directory: etherpad
run: |
docker run --rm -d -p 9001:9001 --name test ${{ env.TEST_TAG }}
./bin/installDeps.sh
docker logs -f test &
while true; do
echo "Waiting for Docker container to start..."
status=$(docker container inspect -f '{{.State.Health.Status}}' test) || exit 1
case ${status} in
healthy) break;;
starting) sleep 2;;
*) printf %s\\n "unexpected status: ${status}" >&2; exit 1;;
esac
done
(cd src && gnpm run test-container)
git clean -dxf .
-
name: Docker meta
if: github.event_name == 'push'
id: meta
uses: docker/metadata-action@v6
with:
images: etherpad/etherpad
tags: |
type=ref,event=branch
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
-
name: Log in to Docker Hub
if: github.event_name == 'push'
uses: docker/login-action@v4
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
-
name: Build and push
id: build-docker
if: github.event_name == 'push'
uses: docker/build-push-action@v7
with:
context: ./etherpad
target: production
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
- name: Update repo description
uses: peter-evans/dockerhub-description@v5
if: github.ref == 'refs/heads/master'
with:
readme-filepath: ./etherpad/README.md
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
repository: etherpad/etherpad
enable-url-completion: true
- name: Check out
if: github.event_name == 'push' && github.ref == 'refs/heads/develop'
uses: actions/checkout@v6
with:
path: ether-charts
repository: ether/ether-charts
token: ${{ secrets.ETHER_CHART_TOKEN }}
- name: Update tag in values-dev.yaml
if: success() && github.ref == 'refs/heads/develop'
working-directory: ether-charts
run: |
sed -i 's/tag: ".*"/tag: "${{ steps.build-docker.outputs.digest }}"/' values-dev.yaml
- name: Commit and push changes
working-directory: ether-charts
if: success() && github.ref == 'refs/heads/develop'
run: |
git config --global user.name 'github-actions[bot]'
git config --global user.email 'github-actions[bot]@users.noreply.github.com'
git add values-dev.yaml
git commit -m 'Update develop image tag'
git push
================================================
FILE: .github/workflows/frontend-admin-tests.yml
================================================
# Leave the powered by Sauce Labs bit in as this means we get additional concurrency
name: "Frontend admin tests"
on:
push:
paths-ignore:
- 'doc/**'
permissions:
contents: read # to fetch code (actions/checkout)
jobs:
withplugins:
env:
PNPM_HOME: ~/.pnpm-store
name: with plugins
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
node: [20, 22, 24]
steps:
-
name: Generate Sauce Labs strings
id: sauce_strings
run: |
printf %s\\n '::set-output name=name::${{ github.workflow }} - ${{ github.job }} - Node ${{ matrix.node }}'
printf %s\\n '::set-output name=tunnel_id::${{ github.run_id }}-${{ github.run_number }}-${{ github.job }}-node${{ matrix.node }}'
-
name: Checkout repository
uses: actions/checkout@v6
- uses: actions/cache@v5
name: Setup gnpm cache
if: always()
with:
path: |
${{ env.PNPM_HOME }}
~/.local/share/gnpm
/usr/local/bin/gnpm
/usr/local/bin/gnpm-0.0.12
key: ${{ runner.os }}-gnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-gnpm-store-
- name: Setup gnpm
uses: SamTV12345/gnpm-setup@main
with:
version: 0.0.12
- name: Cache playwright binaries
uses: actions/cache@v5
id: playwright-cache
with:
path: |
~/.cache/ms-playwright
key: ${{ runner.os }}-playwright-${{ env.PLAYWRIGHT_VERSION }}
#-
# name: Install etherpad plugins
# # We intentionally install an old ep_align version to test upgrades to
# # the minor version number. The --legacy-peer-deps flag is required to
# # work around a bug in npm v7: https://github.com/npm/cli/issues/2199
# run: pnpm install --workspace-root ep_align@0.2.27
# Etherpad core dependencies must be installed after installing the
# plugin's dependencies, otherwise npm will try to hoist common
# dependencies by removing them from src/node_modules and installing them
# in the top-level node_modules. As of v6.14.10, npm's hoist logic appears
# to be buggy, because it sometimes removes dependencies from
# src/node_modules but fails to add them to the top-level node_modules.
# Even if npm correctly hoists the dependencies, the hoisting seems to
# confuse tools such as `npm outdated`, `npm update`, and some ESLint
# rules.
-
name: Install all dependencies and symlink for ep_etherpad-lite
run: gnpm i --runtimeVersion="${{ matrix.node }}"
#-
# name: Install etherpad plugins
# run: rm -Rf node_modules/ep_align/static/tests/*
-
name: export GIT_HASH to env
id: environment
run: echo "::set-output name=sha_short::$(git rev-parse --short ${{ github.sha }})"
-
name: Create settings.json
run: cp settings.json.template settings.json
-
name: Write custom settings.json that enables the Admin UI tests
run: "sed -i 's/\"enableAdminUITests\": false/\"enableAdminUITests\": true,\\n\"users\":{\"admin\":{\"password\":\"changeme1\",\"is_admin\":true}}/' settings.json"
-
name: increase maxHttpBufferSize
run: "sed -i 's/\"maxHttpBufferSize\": 50000/\"maxHttpBufferSize\": 10000000/' settings.json"
-
name: Disable import/export rate limiting
run: |
sed -e '/^ *"importExportRateLimiting":/,/^ *\}/ s/"max":.*/"max": 100000000/' -i settings.json
- name: Build admin frontend
working-directory: admin
run: |
gnpm run build --runtimeVersion="${{ matrix.node }}"
# name: Run the frontend admin tests
# shell: bash
# env:
# SAUCE_USERNAME: ${{ secrets.SAUCE_USERNAME }}
# SAUCE_ACCESS_KEY: ${{ secrets.SAUCE_ACCESS_KEY }}
# SAUCE_NAME: ${{ steps.sauce_strings.outputs.name }}
# TRAVIS_JOB_NUMBER: ${{ steps.sauce_strings.outputs.tunnel_id }}
# GIT_HASH: ${{ steps.environment.outputs.sha_short }}
# run: |
# src/tests/frontend/travis/adminrunner.sh
#-
# uses: saucelabs/sauce-connect-action@v2.3.6
# with:
# username: ${{ secrets.SAUCE_USERNAME }}
# accessKey: ${{ secrets.SAUCE_ACCESS_KEY }}
# tunnelIdentifier: ${{ steps.sauce_strings.outputs.tunnel_id }}
#-
# name: Run the frontend admin tests
# shell: bash
# env:
# SAUCE_USERNAME: ${{ secrets.SAUCE_USERNAME }}
# SAUCE_ACCESS_KEY: ${{ secrets.SAUCE_ACCESS_KEY }}
# SAUCE_NAME: ${{ steps.sauce_strings.outputs.name }}
# TRAVIS_JOB_NUMBER: ${{ steps.sauce_strings.outputs.tunnel_id }}
# GIT_HASH: ${{ steps.environment.outputs.sha_short }}
# run: |
# src/tests/frontend/travis/adminrunner.sh
- name: Run the frontend admin tests
shell: bash
run: |
gnpm run prod --runtimeVersion="${{ matrix.node }}" &
connected=false
can_connect() {
curl -sSfo /dev/null http://localhost:9001/ || return 1
connected=true
}
now() { date +%s; }
start=$(now)
while [ $(($(now) - $start)) -le 15 ] && ! can_connect; do
sleep 1
done
cd src
gnpm exec playwright install --runtimeVersion="${{ matrix.node }}"
gnpm exec playwright install-deps --runtimeVersion="${{ matrix.node }}"
gnpm run test-admin --runtimeVersion="${{ matrix.node }}"
- uses: actions/upload-artifact@v7
if: always()
with:
name: playwright-report-${{ matrix.node }}
path: src/playwright-report/
retention-days: 30
================================================
FILE: .github/workflows/frontend-tests.yml
================================================
# Leave the powered by Sauce Labs bit in as this means we get additional concurrency
name: "Frontend tests powered by Sauce Labs"
on:
push:
paths-ignore:
- 'doc/**'
permissions:
contents: read # to fetch code (actions/checkout)
jobs:
playwright-chrome:
env:
PNPM_HOME: ~/.pnpm-store
name: Playwright Chrome
runs-on: ubuntu-latest
steps:
-
name: Generate Sauce Labs strings
id: sauce_strings
run: |
printf %s\\n '::set-output name=name::${{ github.workflow }} - ${{ github.job }}'
printf %s\\n '::set-output name=tunnel_id::${{ github.run_id }}-${{ github.run_number }}-${{ github.job }}'
-
name: Checkout repository
uses: actions/checkout@v6
- uses: actions/cache@v5
name: Setup gnpm cache
if: always()
with:
path: |
${{ env.PNPM_HOME }}
~/.cache/ms-playwright
~/.local/share/gnpm
/usr/local/bin/gnpm
/usr/local/bin/gnpm-0.0.12
key: ${{ runner.os }}-gnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-gnpm-store-
- name: Setup gnpm
uses: SamTV12345/gnpm-setup@main
with:
version: 0.0.12
-
name: Install all dependencies and symlink for ep_etherpad-lite
run: gnpm install --frozen-lockfile
-
name: export GIT_HASH to env
id: environment
run: echo "::set-output name=sha_short::$(git rev-parse --short ${{ github.sha }})"
-
name: Create settings.json
run: cp ./src/tests/settings.json settings.json
- name: Run the frontend tests
shell: bash
run: |
gnpm run prod &
connected=false
can_connect() {
curl -sSfo /dev/null http://localhost:9001/ || return 1
connected=true
}
now() { date +%s; }
start=$(now)
while [ $(($(now) - $start)) -le 15 ] && ! can_connect; do
sleep 1
done
cd src
gnpm exec playwright install chromium --with-deps
gnpm run test-ui --project=chromium
- uses: actions/upload-artifact@v7
if: always()
with:
name: playwright-report-${{ matrix.node }}-chrome
path: src/playwright-report/
retention-days: 30
playwright-firefox:
env:
PNPM_HOME: ~/.pnpm-store
name: Playwright Firefox
runs-on: ubuntu-latest
steps:
- name: Generate Sauce Labs strings
id: sauce_strings
run: |
printf %s\\n '::set-output name=name::${{ github.workflow }} - ${{ github.job }}'
printf %s\\n '::set-output name=tunnel_id::${{ github.run_id }}-${{ github.run_number }}-${{ github.job }}'
- name: Checkout repository
uses: actions/checkout@v6
- uses: actions/cache@v5
name: Setup gnpm cache
if: always()
with:
path: |
${{ env.PNPM_HOME }}
~/.local/share/gnpm
~/.cache/ms-playwright
/usr/local/bin/gnpm
/usr/local/bin/gnpm-0.0.12
key: ${{ runner.os }}-gnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-gnpm-store-
- name: Setup gnpm
uses: SamTV12345/gnpm-setup@main
with:
version: 0.0.12
- name: Install all dependencies and symlink for ep_etherpad-lite
run: gnpm install --frozen-lockfile
- name: export GIT_HASH to env
id: environment
run: echo "::set-output name=sha_short::$(git rev-parse --short ${{ github.sha }})"
- name: Create settings.json
run: cp ./src/tests/settings.json settings.json
- name: Run the frontend tests
shell: bash
run: |
gnpm run prod &
connected=false
can_connect() {
curl -sSfo /dev/null http://localhost:9001/ || return 1
connected=true
}
now() { date +%s; }
start=$(now)
while [ $(($(now) - $start)) -le 15 ] && ! can_connect; do
sleep 1
done
cd src
gnpm exec playwright install firefox --with-deps
gnpm run test-ui --project=firefox
- uses: actions/upload-artifact@v7
if: always()
with:
name: playwright-report-${{ matrix.node }}-firefox
path: src/playwright-report/
retention-days: 30
playwright-webkit:
name: Playwright Webkit
runs-on: ubuntu-latest
env:
PNPM_HOME: ~/.pnpm-store
steps:
-
name: Generate Sauce Labs strings
id: sauce_strings
run: |
printf %s\\n '::set-output name=name::${{ github.workflow }} - ${{ github.job }}'
printf %s\\n '::set-output name=tunnel_id::${{ github.run_id }}-${{ github.run_number }}-${{ github.job }}'
-
name: Checkout repository
uses: actions/checkout@v6
- uses: actions/cache@v5
name: Setup gnpm cache
if: always()
with:
path: |
${{ env.PNPM_HOME }}
~/.local/share/gnpm
~/.cache/ms-playwright
/usr/local/bin/gnpm
/usr/local/bin/gnpm-0.0.12
key: ${{ runner.os }}-gnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: ${{ runner.os }}-gnpm-store-
- name: Setup gnpm
uses: SamTV12345/gnpm-setup@main
with:
version: 0.0.12
-
name: Install all dependencies and symlink for ep_etherpad-lite
run: gnpm install --frozen-lockfile
-
name: export GIT_HASH to env
id: environment
run: echo "::set-output name=sha_short::$(git rev-parse --short ${{ github.sha }})"
-
name: Create settings.json
run: cp ./src/tests/settings.json settings.json
- name: Run the frontend tests
shell: bash
run: |
gnpm run prod &
connected=false
can_connect() {
curl -sSfo /dev/null http://localhost:9001/ || return 1
connected=true
}
now() { date +%s; }
start=$(now)
while [ $(($(now) - $start)) -le 15 ] && ! can_connect; do
sleep 1
done
cd src
gnpm exec playwright install webkit --with-deps
gnpm run test-ui --project=webkit || true
- uses: actions/upload-artifact@v7
if: always()
with:
name: playwright-report-${{ matrix.node }}-webkit
path: src/playwright-report/
retention-days: 30
================================================
FILE: .github/workflows/handleRelease.yml
================================================
name: "Handle release"
on:
push:
tags:
- 'v*.*.*'
# allow manual triggering of the workflow
workflow_dispatch:
permissions:
contents: read
env:
PNPM_HOME: ~/.pnpm-store
jobs:
create-release:
permissions: write-all
# run on pushes to any branch
# run on PRs from external forks
if: |
(github.event_name != 'pull_request')
|| (github.event.pull_request.head.repo.id != github.event.pull_request.base.repo.id)
name: Handle the release
runs-on: ubuntu-latest
steps:
-
name: Checkout repository
uses: actions/checkout@v6
- uses: actions/cache@v5
name: Setup gnpm cache
if: always()
with:
path: |
${{ env.STORE_PATH }}
~/.local/share/gnpm
~/.cache/ms-playwright
/usr/local/bin/gnpm
/usr/local/bin/gnpm-0.0.12
key: ${{ runner.os }}-gnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-gnpm-store-
- name: Setup gnpm
uses: SamTV12345/gnpm-setup@main
with:
version: 0.0.12
- name: Install all dependencies and symlink for ep_etherpad-lite
run: gnpm install --frozen-lockfile
- name: Build etherpad
run: gnpm run build:etherpad
# On release, create release
- name: Generate Changelog
working-directory: bin
run: gnpm run generateChangelog ${{ github.ref }} > ${{ github.workspace }}-CHANGELOG.txt
- name: Release
uses: softprops/action-gh-release@v2
if: ${{startsWith(github.ref, 'refs/tags/v') }}
with:
body_path: ${{ github.workspace }}-CHANGELOG.txt
make_latest: true
================================================
FILE: .github/workflows/load-test.yml
================================================
name: "Loadtest"
# any branch is useful for testing before a PR is submitted
on:
push:
paths-ignore:
- "doc/**"
pull_request:
paths-ignore:
- "doc/**"
permissions:
contents: read
env:
PNPM_HOME: ~/.pnpm-store
LOG_LEVEL: DEBUG
jobs:
withoutplugins:
# run on pushes to any branch
# run on PRs from external forks
if: |
(github.event_name != 'pull_request')
|| (github.event.pull_request.head.repo.id != github.event.pull_request.base.repo.id)
name: without plugins
runs-on: ubuntu-latest
steps:
-
name: Checkout repository
uses: actions/checkout@v6
- uses: actions/cache@v5
name: Setup gnpm cache
if: always()
with:
path: |
${{ env.STORE_PATH }}
~/.local/share/gnpm
~/.cache/ms-playwright
/usr/local/bin/gnpm
/usr/local/bin/gnpm-0.0.12
key: ${{ runner.os }}-gnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-gnpm-store-
- name: Setup gnpm
uses: SamTV12345/gnpm-setup@main
with:
version: 0.0.12
-
name: Install all dependencies and symlink for ep_etherpad-lite
run: gnpm install --frozen-lockfile
-
name: Install etherpad-load-test
run: sudo npm install -g etherpad-load-test-socket-io
-
name: Run load test
run: |
gnpm --gnpmEnv
eval "$(gnpm --gnpmEnv)"
echo $PATH
src/tests/frontend/travis/runnerLoadTest.sh 25 50
withplugins:
# run on pushes to any branch
# run on PRs from external forks
if: |
(github.event_name != 'pull_request')
|| (github.event.pull_request.head.repo.id != github.event.pull_request.base.repo.id)
name: with Plugins
runs-on: ubuntu-latest
steps:
-
name: Checkout repository
uses: actions/checkout@v6
- uses: actions/cache@v5
name: Setup gnpm cache
if: always()
with:
path: |
${{ env.STORE_PATH }}
~/.local/share/gnpm
~/.cache/ms-playwright
/usr/local/bin/gnpm
/usr/local/bin/gnpm-0.0.12
key: ${{ runner.os }}-gnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-gnpm-store-
- name: Setup gnpm
uses: SamTV12345/gnpm-setup@main
with:
version: 0.0.12
-
name: Install etherpad-load-test
run: sudo npm install -g etherpad-load-test-socket-io
-
name: Install etherpad plugins
# The --legacy-peer-deps flag is required to work around a bug in npm v7:
# https://github.com/npm/cli/issues/2199
run: >
gnpm install --workspace-root
ep_align
ep_author_hover
ep_cursortrace
ep_font_size
ep_hash_auth
ep_headings2
ep_markdown
ep_readonly_guest
ep_set_title_on_pad
ep_spellcheck
ep_subscript_and_superscript
ep_table_of_contents
# Etherpad core dependencies must be installed after installing the
# plugin's dependencies, otherwise npm will try to hoist common
# dependencies by removing them from src/node_modules and installing them
# in the top-level node_modules. As of v6.14.10, npm's hoist logic appears
# to be buggy, because it sometimes removes dependencies from
# src/node_modules but fails to add them to the top-level node_modules.
# Even if npm correctly hoists the dependencies, the hoisting seems to
# confuse tools such as `npm outdated`, `npm update`, and some ESLint
# rules.
-
name: Install all dependencies and symlink for ep_etherpad-lite
run: gnpm install --frozen-lockfile
-
name: Run load test
run: |
eval "$(gnpm --gnpmEnv)"
src/tests/frontend/travis/runnerLoadTest.sh 25 50
long:
# run on pushes to any branch
# run on PRs from external forks
if: |
(github.event_name != 'pull_request')
|| (github.event.pull_request.head.repo.id != github.event.pull_request.base.repo.id)
name: long running
runs-on: ubuntu-latest
steps:
-
name: Checkout repository
uses: actions/checkout@v6
- uses: actions/cache@v5
name: Setup gnpm cache
if: always()
with:
path: |
${{ env.STORE_PATH }}
~/.local/share/gnpm
~/.cache/ms-playwright
/usr/local/bin/gnpm
/usr/local/bin/gnpm-0.0.12
key: ${{ runner.os }}-gnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-gnpm-store-
- name: Setup gnpm
uses: SamTV12345/gnpm-setup@main
with:
version: 0.0.12
-
name: Install all dependencies and symlink for ep_etherpad-lite
run: gnpm install --frozen-lockfile
-
name: Install etherpad-load-test
run: sudo npm install -g etherpad-load-test-socket-io
-
name: Run load test
run: |
gnpm --gnpmEnv
eval "$(gnpm --gnpmEnv)"
echo $PATH
src/tests/frontend/travis/runnerLoadTest.sh 5000 5
================================================
FILE: .github/workflows/perform-type-check.yml
================================================
name: "Perform type checks"
# any branch is useful for testing before a PR is submitted
on:
push:
paths-ignore:
- "doc/**"
pull_request:
paths-ignore:
- "doc/**"
permissions:
contents: read
env:
PNPM_HOME: ~/.pnpm-store
jobs:
performTypeCheck:
if: |
(github.event_name != 'pull_request')
|| (github.event.pull_request.head.repo.id != github.event.pull_request.base.repo.id)
name: perform type check
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6
- uses: actions/cache@v5
name: Setup gnpm cache
if: always()
with:
path: |
${{ env.STORE_PATH }}
~/.local/share/gnpm
~/.cache/ms-playwright
/usr/local/bin/gnpm
/usr/local/bin/gnpm-0.0.12
key: ${{ runner.os }}-gnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-gnpm-store-
- name: Setup gnpm
uses: SamTV12345/gnpm-setup@main
with:
version: 0.0.12
-
name: Install all dependencies and symlink for ep_etherpad-lite
run: gnpm install --frozen-lockfile
- name: Perform type check
working-directory: ./src
run: gnpm run ts-check
================================================
FILE: .github/workflows/rate-limit.yml
================================================
name: "rate limit"
# any branch is useful for testing before a PR is submitted
on:
push:
paths-ignore:
- "doc/**"
pull_request:
paths-ignore:
- "doc/**"
permissions:
contents: read
env:
PNPM_HOME: ~/.pnpm-store
jobs:
ratelimit:
# run on pushes to any branch
# run on PRs from external forks
if: |
(github.event_name != 'pull_request')
|| (github.event.pull_request.head.repo.id != github.event.pull_request.base.repo.id)
name: test
runs-on: ubuntu-latest
steps:
-
name: Checkout repository
uses: actions/checkout@v6
- uses: actions/cache@v5
name: Setup gnpm cache
if: always()
with:
path: |
${{ env.STORE_PATH }}
~/.local/share/gnpm
~/.cache/ms-playwright
/usr/local/bin/gnpm
/usr/local/bin/gnpm-0.0.12
key: ${{ runner.os }}-gnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-gnpm-store-
- name: Setup gnpm
uses: SamTV12345/gnpm-setup@main
with:
version: 0.0.12
-
name: docker network
run: docker network create --subnet=172.23.0.0/16 ep_net
-
name: build docker image
run: |
docker build -f Dockerfile -t epl-debian-slim --build-arg NODE_ENV=develop .
docker build -f src/tests/ratelimit/Dockerfile.nginx -t nginx-latest .
docker build -f src/tests/ratelimit/Dockerfile.anotherip -t anotherip .
-
name: run docker images
run: |
docker run --name etherpad-docker -p 9000:9001 --rm --network ep_net --ip 172.23.42.2 -e 'TRUST_PROXY=true' epl-debian-slim &
docker run -p 8081:80 --rm --network ep_net --ip 172.23.42.1 -d nginx-latest
docker run --rm --network ep_net --ip 172.23.42.3 --name anotherip -dt anotherip
-
name: install dependencies and create symlink for ep_etherpad-lite
run: gnpm install --frozen-lockfile
-
name: run rate limit test
run: |
cd src/tests/ratelimit
./testlimits.sh
================================================
FILE: .github/workflows/release.yml
================================================
permissions:
contents: read
name: Release etherpad
on:
workflow_dispatch:
inputs:
release_type:
description: 'Choose the type of release to create'
required: true
default: 'patch'
type: choice
options:
- patch
- minor
- major
env:
PNPM_HOME: ~/.pnpm-store
jobs:
releases:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6
with:
repository: ether/etherpad-lite
path: etherpad
token: '${{ secrets.ETHER_RELEASE_TOKEN }}'
fetch-depth: '0'
fetch-tags: 'true'
- name: Checkout master
working-directory: etherpad
run: |
git fetch origin master
git checkout master
git reset --hard origin/master
- name: Checkout develop
working-directory: etherpad
run: |
git fetch origin develop
git checkout develop
git reset --hard origin/develop
- name: Checkout repository
uses: actions/checkout@v6
with:
repository: ether/ether.github.com
path: ether.github.com
token: '${{ secrets.ETHER_RELEASE_TOKEN }}'
- uses: actions/cache@v5
name: Setup gnpm cache
if: always()
with:
path: |
${{ env.STORE_PATH }}
~/.local/share/gnpm
~/.cache/ms-playwright
/usr/local/bin/gnpm
/usr/local/bin/gnpm-0.0.12
key: ${{ runner.os }}-gnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-gnpm-store-
- name: Setup gnpm
uses: SamTV12345/gnpm-setup@main
with:
version: 0.0.12
- name: Install dependencies ether.github.com
run: gnpm install --frozen-lockfile
working-directory: ether.github.com
- name: Set git user
run: |
git config --global user.name "Etherpad Release Bot"
git config --global user.email "noreply@etherpad.org"
- uses: ruby/setup-ruby@v1
with:
ruby-version: 2.7
- uses: reitzig/actions-asciidoctor@v2.0.4
with:
version: 2.0.18
- name: Prepare release
working-directory: etherpad
run: |
cd bin
gnpm install
gnpm run release ${{ inputs.release_type }}
- name: Push after release
working-directory: etherpad
run: |
./bin/push-after-release.sh
================================================
FILE: .github/workflows/releaseEtherpad.yml
================================================
name: releaseEtherpad.yaml
permissions:
contents: read
on:
workflow_dispatch:
env:
PNPM_HOME: ~/.pnpm-store
jobs:
release:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Get pnpm store directory
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- uses: actions/cache@v5
name: Setup pnpm cache
if: always()
with:
path: |
${{ env.STORE_PATH }}
~/.local/share/gnpm
/usr/local/bin/gnpm
/usr/local/bin/gnpm-0.0.12
key: ${{ runner.os }}-gnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-gnpm-store-
- name: Setup gnpm
uses: SamTV12345/gnpm-setup@main
with:
version: 0.0.12
- name: Install dependencies
run: gnpm install --frozen-lockfile
- name: Rename etherpad
working-directory: ./src
run: sed -i 's/ep_etherpad-lite/ep_etherpad/g' package.json
- name: Release to npm
run: gnpm publish --no-git-checks
working-directory: ./src
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_PRIVATE_TOKEN }}
================================================
FILE: .github/workflows/stale.yml
================================================
name: 'Close stale issues and PRs'
on:
schedule:
- cron: '30 6 * * *'
permissions:
issues: write
pull-requests: write
jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v10
with:
close-issue-label: wontfix
close-pr-label: wontfix
days-before-close: -1
exempt-issue-labels: 'pinned,security,Bug,Serious Bug,Minor bug,Black hole bug,Special case Bug,Upstream bug,Feature Request'
exempt-pr-labels: 'pinned,security,Bug,Serious Bug,Minor bug,Black hole bug,Special case Bug,Upstream bug,Feature Request'
================================================
FILE: .github/workflows/upgrade-from-latest-release.yml
================================================
name: "Upgrade from latest release"
# any branch is useful for testing before a PR is submitted
on:
push:
paths-ignore:
- "doc/**"
pull_request:
paths-ignore:
- "doc/**"
permissions:
contents: read
env:
PNPM_HOME: ~/.pnpm-store
jobs:
withpluginsLinux:
# run on pushes to any branch
# run on PRs from external forks
if: |
(github.event_name != 'pull_request')
|| (github.event.pull_request.head.repo.id != github.event.pull_request.base.repo.id)
name: Linux with Plugins
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
node: [20, 22, 24]
steps:
-
name: Check out latest release
uses: actions/checkout@v6
with:
ref: develop #FIXME change to master when doing release
- uses: actions/cache@v5
name: Setup gnpm cache
if: always()
with:
path: |
${{ env.STORE_PATH }}
~/.local/share/gnpm
~/.cache/ms-playwright
/usr/local/bin/gnpm
/usr/local/bin/gnpm-0.0.12
key: ${{ runner.os }}-gnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-gnpm-store-
- name: Setup gnpm
uses: SamTV12345/gnpm-setup@main
with:
version: 0.0.12
- name: Install libreoffice
uses: awalsh128/cache-apt-pkgs-action@v1.6.0
with:
packages: libreoffice libreoffice-pdfimport
version: 1.0
-
name: Install libreoffice
uses: awalsh128/cache-apt-pkgs-action@v1.6.0
with:
packages: libreoffice libreoffice-pdfimport
version: 1.0
-
name: Install all dependencies and symlink for ep_etherpad-lite
run: gnpm install --frozen-lockfile --runtimeVersion="${{ matrix.node }}"
- name: Build admin ui
working-directory: admin
run: gnpm build --runtimeVersion="${{ matrix.node }}"
-
name: Install Etherpad plugins
run: >
gnpm run install-plugins
ep_align
ep_author_hover
ep_cursortrace
ep_font_size
ep_hash_auth
ep_headings2
ep_markdown
ep_readonly_guest
ep_set_title_on_pad
ep_spellcheck
ep_subscript_and_superscript
ep_table_of_contents --runtimeVersion="${{ matrix.node }}"
-
name: Run the backend tests
run: gnpm run test --runtimeVersion="${{ matrix.node }}"
-
name: Install all dependencies and symlink for ep_etherpad-lite
run: gnpm install --frozen-lockfile --runtimeVersion="${{ matrix.node }}"
# Because actions/checkout@v6 is called with "ref: master" and without
# "fetch-depth: 0", the local clone does not have the ${GITHUB_SHA}
# commit. Fetch ${GITHUB_REF} to get the ${GITHUB_SHA} commit. Note that a
# plain "git fetch" only fetches "normal" references (refs/heads/* and
# refs/tags/*), and for pull requests none of the normal references
# include ${GITHUB_SHA}, so we have to explicitly tell Git to fetch
# ${GITHUB_REF}.
-
name: Fetch the new Git commits
run: git fetch --depth=1 origin "${GITHUB_REF}"
-
name: Upgrade to the new Git revision
# For pull requests, ${GITHUB_SHA} is the automatically generated merge
# commit that merges the PR's source branch to its destination branch.
run: git checkout "${GITHUB_SHA}"
- name: Run the backend tests
run: gnpm run test --runtimeVersion="${{ matrix.node }}"
================================================
FILE: .gitignore
================================================
/etherpad-win.exe
/etherpad-win.zip
node_modules
/settings.json
!settings.json.template
APIKEY.txt
SESSIONKEY.txt
var/dirty.db
.env
*~
*.patch
npm-debug.log
*.DS_Store
*.crt
*.key
credentials.json
out/
.nyc_output
.idea
/package-lock.json
/src/bin/abiword.exe
/src/bin/convertSettings.json
/src/bin/etherpad-1.deb
/src/bin/node.exe
plugin_packages
/src/templates/admin
/src/test-results
playwright-report
state.json
/src/static/oidc
================================================
FILE: .lgtm.yml
================================================
extraction:
javascript:
index:
exclude:
- src/static/js/vendors
python:
index:
exclude:
- /
================================================
FILE: .pr_agent.toml
================================================
[pr_reviewer]
run_on_pr_sync = true
[pr_description]
run_on_pr_sync = true
================================================
FILE: .travis.yml
================================================
language: node_js
node_js:
- "lts/*"
services:
- docker
cache: false
env:
global:
- secure: "WMGxFkOeTTlhWB+ChMucRtIqVmMbwzYdNHuHQjKCcj8HBEPdZLfCuK/kf4rG\nVLcLQiIsyllqzNhBGVHG1nyqWr0/LTm8JRqSCDDVIhpyzp9KpCJQQJG2Uwjk\n6/HIJJh/wbxsEdLNV2crYU/EiVO3A4Bq0YTHUlbhUqG3mSCr5Ec="
- secure: "gejXUAHYscbR6Bodw35XexpToqWkv2ifeECsbeEmjaLkYzXmUUNWJGknKSu7\nEUsSfQV8w+hxApr1Z+jNqk9aX3K1I4btL3cwk2trnNI8XRAvu1c1Iv60eerI\nkE82Rsd5lwUaMEh+/HoL8ztFCZamVndoNgX7HWp5J/NRZZMmh4g="
_set_loglevel_warn: &set_loglevel_warn |
sed -e 's/"loglevel":[^,]*/"loglevel": "WARN"/' \
settings.json.template >settings.json.template.new &&
mv settings.json.template.new settings.json.template
_enable_admin_tests: &enable_admin_tests |
sed -e 's/"enableAdminUITests": false/"enableAdminUITests": true,\n"users":{"admin":{"password":"changeme","is_admin":true}}/' \
settings.json.template >settings.json.template.new &&
mv settings.json.template.new settings.json.template
_install_libreoffice: &install_libreoffice >-
sudo add-apt-repository -y ppa:libreoffice/ppa &&
sudo apt-get update &&
sudo apt-get -y install libreoffice libreoffice-pdfimport
# The --legacy-peer-deps flag is required to work around a bug in npm v7:
# https://github.com/npm/cli/issues/2199
_install_plugins: &install_plugins >-
npm install --no-save --legacy-peer-deps
ep_align
ep_author_hover
ep_cursortrace
ep_font_size
ep_hash_auth
ep_headings2
ep_markdown
ep_readonly_guest
ep_spellcheck
ep_subscript_and_superscript
ep_table_of_contents
ep_set_title_on_pad
jobs:
include:
# we can only frontend tests from the ether/ organization and not from forks.
# To request tests to be run ask a maintainer to fork your repo to ether/
- if: fork = false
name: "Test the Frontend without Plugins"
install:
- *set_loglevel_warn
- *enable_admin_tests
- "src/tests/frontend/travis/sauce_tunnel.sh"
- "bin/installDeps.sh"
- "export GIT_HASH=$(git rev-parse --verify --short HEAD)"
script:
- "./src/tests/frontend/travis/runner.sh"
- name: "Run the Backend tests without Plugins"
install:
- *install_libreoffice
- *set_loglevel_warn
- "bin/installDeps.sh"
- "cd src && pnpm install && cd -"
script:
- "cd src && pnpm test"
- name: "Test the Dockerfile"
install:
- "cd src && pnpm install && cd -"
script:
- "docker build -t etherpad:test ."
- "docker run -d -p 9001:9001 etherpad:test && sleep 3"
- "cd src && pnpm run test-container"
- name: "Load test Etherpad without Plugins"
install:
- *set_loglevel_warn
- "bin/installDeps.sh"
- "cd src && pnpm install && cd -"
- "npm install -g etherpad-load-test"
script:
- "src/tests/frontend/travis/runnerLoadTest.sh"
# we can only frontend tests from the ether/ organization and not from forks.
# To request tests to be run ask a maintainer to fork your repo to ether/
- if: fork = false
name: "Test the Frontend Plugins only"
install:
- *set_loglevel_warn
- *enable_admin_tests
- "src/tests/frontend/travis/sauce_tunnel.sh"
- "bin/installDeps.sh"
- "rm src/tests/frontend/specs/*"
- *install_plugins
- "export GIT_HASH=$(git rev-parse --verify --short HEAD)"
script:
- "./src/tests/frontend/travis/runner.sh"
- name: "Lint test package-lock.json"
install:
- "npm install lockfile-lint"
script:
- npx lockfile-lint --path src/package-lock.json --validate-https --allowed-hosts npm
- name: "Run the Backend tests with Plugins"
install:
- *install_libreoffice
- *set_loglevel_warn
- "bin/installDeps.sh"
- *install_plugins
- "cd src && pnpm install && cd -"
script:
- "cd src && pnpm test"
- name: "Test the Dockerfile"
install:
- "cd src && pnpm install && cd -"
script:
- "docker build -t etherpad:test ."
- "docker run -d -p 9001:9001 etherpad:test && sleep 3"
- "cd src && pnpm run test-container"
- name: "Load test Etherpad with Plugins"
install:
- *set_loglevel_warn
- "bin/installDeps.sh"
- *install_plugins
- "cd src && npm install && cd -"
- "npm install -g etherpad-load-test"
script:
- "src/tests/frontend/travis/runnerLoadTest.sh"
- name: "Test rate limit"
install:
- "docker network create --subnet=172.23.42.0/16 ep_net"
- "docker build -f Dockerfile -t epl-debian-slim ."
- "docker build -f src/tests/ratelimit/Dockerfile.nginx -t nginx-latest ."
- "docker build -f src/tests/ratelimit/Dockerfile.anotherip -t anotherip ."
- "docker run -p 8081:80 --rm --network ep_net --ip 172.23.42.1 -d nginx-latest"
- "docker run --name etherpad-docker -p 9000:9001 --rm --network ep_net --ip 172.23.42.2 -e 'TRUST_PROXY=true' epl-debian-slim &"
- "docker run --rm --network ep_net --ip 172.23.42.3 --name anotherip -dt anotherip"
- "./bin/installDeps.sh"
script:
- "cd src/tests/ratelimit && bash testlimits.sh"
notifications:
irc:
channels:
- "irc.freenode.org#etherpad-lite-dev"
================================================
FILE: AGENTS.MD
================================================
# Agent Guide - Etherpad
Welcome to the Etherpad project. This guide provides essential context and instructions for AI agents and developers to effectively contribute to the codebase.
## Project Overview
Etherpad is a real-time collaborative editor designed to be lightweight, scalable, and highly extensible via plugins.
## Technical Stack
- **Runtime:** Node.js
- **Package Manager:** pnpm
- **Languages:** TypeScript (primary), JavaScript, CSS, HTML
- **Backend:** Express.js, Socket.io
- **Frontend:** Legacy core (`src/static`), New UI (`ui/`), Admin UI (`admin/`)
- **Build Tools:** Vite (for `ui` and `admin`), custom scripts in `bin/`
- **Testing:** Mocha (Backend), Vitest, Playwright, custom backend test suite
## Directory Structure
- `src/node/`: Backend logic, API, and database abstraction.
- `src/static/`: Core frontend logic (Legacy).
- `ui/`: Modern React-based user interface.
- `admin/`: Modern React-based administration interface.
- `bin/`: Utility and lifecycle scripts.
- `doc/`: Documentation in Markdown and Adoc formats.
- `src/tests/`: Test suites (backend, frontend, UI, admin).
- `var/`: Runtime data (logs, dirtyDB, etc. - ignored by git).
- `local_plugins/`: Directory for developing and testing plugins locally.
## Core Mandates & Conventions
### Coding Style
- **Indentation:** 2 spaces for all files (JS/TS/CSS/HTML).
- **No Tabs:** Use spaces only.
- **Comments:** Provide clear comments for complex logic.
- **Backward Compatibility:** Always ensure compatibility with older versions of the database and configuration files.
### Development Workflow
- **Branching:** Work in feature branches. Issue PRs against the `develop` branch.
- **Commits:** Maintain a linear history (no merge commits). Use meaningful messages in the format: `submodule: description`.
- **Feature Flags:** New features should be placed behind feature flags and disabled by default.
- **Deprecation:** Never remove features abruptly; deprecate them first with a `WARN` log.
### Testing & Validation
- **Requirement:** Every bug fix MUST include a regression test in the same commit.
- **Backend Tests:** Run `pnpm --filter ep_etherpad-lite run test` from the root.
- **Frontend Tests:** Accessible at `/tests/frontend` on a running instance.
- **Linting:** Run `pnpm run lint` to ensure style compliance.
- **Build:** Run `pnpm run build:etherpad` before production deployment.
## Key Concepts
### Easysync
The real-time synchronization engine. It is complex; refer to `doc/public/easysync/` before modifying core synchronization logic.
### Plugin Framework
Most functionality should be implemented as plugins (`ep_...`). Avoid modifying the core unless absolutely necessary.
### Settings
Configured via `settings.json`. A template is available at `settings.json.template`. Environment variables can override any setting using `"${ENV_VAR}"` or `"${ENV_VAR:default_value}"`.
## AI-Specific Guidance
AI/Agent contributions are explicitly welcomed by the maintainers, provided they strictly adhere to the guidelines in `CONTRIBUTING.md` and this guide. Always prioritize stability, readability, and compatibility.
================================================
FILE: CHANGELOG.md
================================================
# 2.6.1
For those wondering where the new updates are and why it was very quite throughout the last 1 1/2 years – I've been working on a new implementation of Etherpad from scratch in Go. It's called Etherpad-Go and you can find it here: `https://github.com/ether/etherpad-go`
and a short FAQ about it here: https://github.com/ether/etherpad-go/wiki/FAQ . I'd love to hear your feedback about it either on Discord or issue tracker. There is a README.md that explains how to get started and try it out and also the FAQ can be quite fruitful. Latest release can be found here: https://github.com/ether/etherpad-go/releases/tag/v0.0.4
### Notable enhancements and fixes of this release
- Minor fixes and improvements to the session transfer feature introduced in 2.6.0
- Dependencies upgrades
# 2.6.0
### Notable enhancements and fixes
- Added native option to transfer your Etherpad session between browsers. If you use multiple browsers or different PC for Etherpad they are different sessions. Meaning typing on one PC and then switching to another one in the same pad will result in different authorship colors. With this new feature you can now transfer your session to another browser or PC. To do so, open the home page and click on the wheel icon in the top right corner. After that click through the first dialog prompting you to copy a code to your clipboard. On your second browser open the same dialog and switch to "Receive Session" tab. There you can paste the code you copied before and click on "Receive Session". After that your session is transferred, and you can continue editing with the same authorship color as before. Just be aware that you can't have two active sessions at once in a pad.
- Updated to oidc provider v2.6.0 after resolving compatibility issues.
🎉 For all the people celebrating: Have a happy and awesome new year! 🎉 There is something big on the horizon for Etherpad in 2026. Stay tuned!
# 2.5.3
### Notable enhancements and fixes
- Fixed an issue with the release script that caused the release to not be created correctly.
# 2.5.2
### Notable enhancements and fixes
- Fixes the no skin theme having an overlapping
- Adds a new setting to disable recent pads to be shown. By setting `showRecentPads` to false in the `settings.json` file you can disable the recent pads feature on the home screen.
- Sets the oidc-provider version to 9.5.1 as 9.5.2 crashes Etherpad on startup.
# 2.5.1
### Notable enhancements and fixes
- Added endpoint for prometheus scraping. You can now scrape the metrics endpoint with prometheus. It is available at /stats/prometheus if you have enableMetrics set to true in your settings.json
- fixed exposeVersion causing the pad panel to not load correctly
- fixed admin manage pad url to also take the base path into account
# 2.5.0
### Notable enhancements and fixes
- Updated to express 5.0.0. This is a major update to express that brings a lot of improvements and fixes. Please update all your plugins to the latest version to ensure compatibility. A lot changed in the route matching, and thus old plugins will throw errors and crash Etherpad.
- Fixed an issue with the no-skin theme with cookie recentPadList feature
- Fixed layout issues with the no-skin theme
# 2.4.2
### Notable enhancements and fixes
- Fixed a german translation in the english translation file.
# 2.4.1
### Notable enhancements and fixes
- Added generating release through ci cd pipeline.
- Readded temporarily disabled workflows after release generation works again
# 2.4.0
### Notable enhancements and fixes
- Added home button to the pad panel. To show it in your current instance, please copy the updated "toolbar" settings from the `settings.json.template` file to your `settings.json` file.
- Added more current design for the default collibri theme.
- Added handling of recent visited pads in the collibri theme. You can now access the most recent three pads you visited in the pad panel.
- Disable stats endpoints if enableMetrics is set to false. This allows you to disable the metrics endpoints if you don't want to use them.
- Use Node LTS instead of always latest Node version.
# 2.3.2
### Notable enhancements and fixes
- Fixed admin ui displaying incorrect text
# 2.3.1
### Notable enhancements and fixes
- Dependency updates
# 2.3.0
### Notable enhancements and fixes
- Added possibility to cluster Etherpads behind reverse proxy. There is now a new reverse proxy designed for Etherpads that handles multiple Etherpads and the created pads in them. It will assign the pad assignement to an Etherpad at random but once the choice was made it will always reverse proxy the same backend. This allows to host multiple concurrent Etherpads and benefit from multi core systems even though one Etherpad is singlethreaded.
- Added reverse proxy configuration for replacing Nginx. In the past there were some issues with nginx and its configuration. This reverse proxy allows you to handle your configuration with ease.
If you want to find out more about the reverse proxy method check out the repository https://github.com/ether/etherpad-proxy . It also contains a sample docker-compose file with three Etherpads and one etherpad-proxy. Of course you need to adapt the settings.json.template to your liking and map it into the reverse proxy image before you are ready :).
- Added client authorization to work with Etherpad. Before it would get blocked because it doesn't have the required claim. As this is now fixed etherpad-proxy can also work with your new OAuth2 configuration and retrieve a token via client credentials flow.
# 2.2.7
### Notable enhancements and fixes
- We migrated all important pages to React 19 and React Router v7
Besides that only dependency updates.
-> Have a merry Christmas and a happy new year. 🎄 🎁
# 2.2.6
### Notable enhancements and fixes
- Added option to delete a pad by the creator. This option can be found in the settings menu. When you click on it you get a confirm dialog and after that you have the chance to completely erase the pad.
# 2.2.5
### Notable enhancements and fixes
- Fixed timeslider not scrolling when the revision count is a multiple of 100
- Added new Restful API for version 2 of Etherpad. It is available at /api-docs
# 2.2.4
### Notable enhancements and fixes
- Switched to new SQLite backend
- Fixed rusty-store-kv module not found
# 2.2.3
### Notable enhancements and fixes
- Introduced a new in process database `rustydb` that represents a fast key value store written in Rust.
- Readded window._ as a shortcut for getting text
- Added support for migrating any ueberdb database to another. You can now switch as you please. See here: https://docs.etherpad.org/cli.html
- Further Typescript movements
- A lot of security issues fixed and reviewed in this release. Please update.
# 2.2.2
### Notable enhancements and fixes
- Removal of Etherpad require kernel: We finally managed to include esbuild to bundle our frontend code together. So no matter how many plugins your server has it is always one JavaScript file. This boosts performance dramatically.
- Added log layoutType: This lets you print the log in either colored or basic (black and white text)
- Introduced esbuild for bundling CSS files
- Cache all files to be bundled in memory for faster load speed
# 2.1.1
### Notable enhancements and fixes
- Fixed failing Docker build when checked out as git submodule. Thanks to @neurolabs
- Fixed: Fallback to websocket and polling when unknown(old) config is present for socket io
- Fixed: Next page disabled if zero page by @samyakj023
- On CTRL+CLICK bring the window back to focus by Helder Sepulveda
# 2.1.0
### Notable enhancements and fixes
- Added PWA support. You can now add your Etherpad instance to your home screen on your mobile device or desktop.
- Fixed live plugin manager versions clashing. Thanks to @yacchin1205
- Fixed a bug in the pad panel where pagination was not working correctly when sorting by pad name
### Compatibility changes
- Reintroduced APIKey.txt support. You can now switch between APIKey and OAuth2.0 authentication. This can be toggled with the setting authenticationMethod. The default is OAuth2. If you want to use the APIKey method you can set that to `apikey`.
# 2.0.3
### Notable enhancements and fixes
- Added documentation for replacing apikeys with oauth2
- Bumped live plugin manager to 0.20.0. Thanks to @fgreinacher
- Added better documentation for using docker-compose with Etherpad
# 2.0.2
### Notable enhancements and fixes
- Fixed the locale loading in the admin panel
- Added OAuth2.0 support for the Etherpad API. You can now log in into the Etherpad API with your admin user using OAuth2
### Compatibility changes
- The tests now require generating a token from the OAuth secret. You can find the `generateJWTToken` in the common.ts script for plugin endpoint updates.
# 2.0.1
### Notable enhancements and fixes
- Fixed a bug where a plugin depending on a scoped dependency would not install successfully.
# 2.0.0
### Compatibility changes
- Socket io has been updated to 4.7.5. This means that the json.send function won't work anymore and needs to be changed to .emit('message', myObj)
- Deprecating npm version 6 in favor of pnpm: We have made the decision to switch to the well established pnpm (https://pnpm.io/). It works by symlinking dependencies into a global directory allowing you to have a cleaner and more reliable environment.
- Introducing Typescript to the Etherpad core: Etherpad core logic has been rewritten in Typescript allowing for compiler checking of errors.
- Rewritten Admin Panel: The Admin panel has been rewritten in React and now features a more pleasant user experience. It now also features an integrated pad searching with sorting functionality.
### Notable enhancements and fixes
* Bugfixes
- Live Plugin Manager: The live plugin manager caused problems when a plugin had depdendencies defined. This issue is now resolved.
* Enhancements
- pnpm Workspaces: In addition to pnpm we introduced workspaces. A clean way to manage multiple bounded contexts like the admin panel or the bin folder.
- Bin folder: The bin folder has been moved from the src folder to the root folder. This change was necessary as the contained scripts do not represent core functionality of the user.
- Starting Etherpad: Etherpad can now be started with a single command: `pnpm run prod` in the root directory.
- Installing Etherpad: Etherpad no longer symlinks itself in the root directory. This is now also taken care by pnpm, and it just creates a node_modules folder with the src directory`s ep_etherpad-lite folder
- Plugins can now be installed simply via the command: `pnpm run plugins i first-plugin second-plugin` or if you want to install from path you can do:
`pnpm run plugins i --path ../path-to-plugin`
# 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
* Prevent etherpad crash when update server is not reachable
* Use npm@6 in Docker build
* Fix setting the log level in settings.json
# 1.9.5
### 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.
### Notable enhancements and fixes
* The support for the tidy program to tidy up HTML files has been removed. This decision was made because it hasn't been updated for years and also caused an incompability when exporting a pad with Abiword.
# 1.9.4
### 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)
### Notable enhancements and fixes
* Fix for MySQL: The logger calls were incorrectly configured leading to a crash when e.g. somebody uses a different encoding than standard MySQL encoding.
# 1.9.3
### Compability changes
* express-rate-limit has been bumped to 7.0.0: This involves the breaking change that "max: 0"
in the importExportRateLimiting is set to always trigger. So set it to your desired value.
If you haven't changed that value in the settings.json you are all set.
### Notable enhancements and fixes
* Bugfixes
* Fix etherpad crashing with mongodb database
* Enhancements
* Add surrealdb database support. You can find out more about this database [here](https://surrealdb.com).
* Make sqlite faster: The sqlite library has been switched to better-sqlite3. This should lead to better performance.
# 1.9.2
### Notable enhancements and fixes
* Security
* Enable session key rotation: This setting can be enabled in the settings.json. It changes the signing key for the cookie authentication in a fixed interval.
* Bugfixes
* Fix appendRevision when creating a new pad via the API without a text.
* Enhancements
* Bump JQuery to version 3.7
* Update elasticsearch connector to version 8
### Compatibility changes
* No compability changes as JQuery maintains excellent backwards compatibility.
#### For plugin authors
* Please update to JQuery 3.7. There is an excellent deprecation guide over [here](https://api.jquery.com/category/deprecated/). Version 3.1 to 3.7 are relevant for the upgrade.
# 1.9.1
### Notable enhancements and fixes
* Security
* Limit requested revisions in timeslider and export to head revision. (affects v1.9.0)
* Bugfixes
* revisions in `CHANGESET_REQ` (timeslider) and export (txt, html, custom)
are now checked to be numbers.
* bump sql for audit fix
* Enhancements
* Add keybinding meta-backspace to delete to beginning of line
* Fix automatic Windows build via GitHub Actions
* Enable docs to be build cross platform thanks to asciidoctor
### Compatibility changes
* tests: drop windows 7 test coverage & use chrome latest for admin tests
* Require Node 16 for Etherpad and target Node 20 for testing
# 1.9.0
### Notable enhancements and fixes
* Windows build:
* The bundled `node.exe` was upgraded from v12 to v16.
* The bundled `node.exe` is now a 64-bit executable. If you need the 32-bit
version you must download and install Node.js yourself.
* Improvements to login session management:
* `express_sid` cookies and `sessionstorage:*` database records are no longer
created unless `requireAuthentication` is `true` (or a plugin causes them to
be created).
* Login sessions now have a finite lifetime by default (10 days after
leaving).
* `sessionstorage:*` database records are automatically deleted when the login
session expires (with some exceptions that will be fixed in the future).
* Requests for static content (e.g., `/robots.txt`) and special pages (e.g.,
the HTTP API, `/stats`) no longer create login session state.
* The secret used to sign the `express_sid` cookie is now automatically
regenerated every day (called *key rotation*) by default. If key rotation is
enabled, the now-deprecated `SESSIONKEY.txt` file can be safely deleted
after Etherpad starts up (its content is read and saved to the database and
used to validate signatures from old cookies until they expire).
* The following settings from `settings.json` are now applied as expected (they
were unintentionally ignored before):
* `padOptions.lang`
* `padOptions.showChat`
* `padOptions.userColor`
* `padOptions.userName`
* HTTP API:
* Fixed the return value of `getText` when called with a specific revision.
* Fixed a potential attribute pool corruption bug with
`copyPadWithoutHistory`.
* Mappings created by `createGroupIfNotExistsFor` are now removed from the
database when the group is deleted.
* Fixed race conditions in the `setText`, `appendText`, and `restoreRevision`
functions.
* Added an optional `authorId` parameter to `appendText`,
`copyPadWithoutHistory`, `createGroupPad`, `createPad`, `restoreRevision`,
`setHTML`, and `setText`, and bumped the latest API version to 1.3.0.
* Fixed a crash if the database is busy enough to cause a query timeout.
* New `/health` endpoint for getting information about Etherpad's health (see
[draft-inadarei-api-health-check-06](https://www.ietf.org/archive/id/draft-inadarei-api-health-check-06.html)).
* Docker now uses the new `/health` endpoint for health checks, which avoids
issues when authentication is enabled. It also avoids the unnecessary creation
of database records for managing browser sessions.
* When copying a pad, the pad's records are copied in batches to avoid database
timeouts with large pads.
* Exporting a large pad to `.etherpad` format should be faster thanks to bulk
database record fetches.
* When importing an `.etherpad` file, records are now saved to the database in
batches to avoid database timeouts with large pads.
#### For plugin authors
* New `expressPreSession` server-side hook.
* Pad server-side hook changes:
* `padCheck`: New hook.
* `padCopy`: New `srcPad` and `dstPad` context properties.
* `padDefaultContent`: New hook.
* `padRemove`: New `pad` context property.
* The `db` property on Pad objects is now public.
* New `getAuthorId` server-side hook.
* New APIs for processing attributes: `ep_etherpad-lite/static/js/attributes`
(low-level API) and `ep_etherpad-lite/static/js/AttributeMap` (high-level
API).
* The `import` server-side hook has a new `ImportError` context property.
* New `exportEtherpad` and `importEtherpad` server-side hooks.
* The `handleMessageSecurity` and `handleMessage` server-side hooks have a new
`sessionInfo` context property that includes the user's author ID, the pad ID,
and whether the user only has read-only access.
* The `handleMessageSecurity` server-side hook can now be used to grant write
access for the current message only.
* The `init_<pluginName>` server-side hooks have a new `logger` context
property that plugins can use to log messages.
* Prevent infinite loop when exiting the server
* Bump dependencies
### Compatibility changes
* Node.js v14.15.0 or later is now required.
* The default login session expiration (applicable if `requireAuthentication` is
`true`) changed from never to 10 days after the user leaves.
#### For plugin authors
* The `client` context property for the `handleMessageSecurity` and
`handleMessage` server-side hooks is deprecated; use the `socket` context
property instead.
* Pad server-side hook changes:
* `padCopy`:
* The `originalPad` context property is deprecated; use `srcPad` instead.
* The `destinationID` context property is deprecated; use `dstPad.id`
instead.
* `padCreate`: The `author` context property is deprecated; use the new
`authorId` context property instead. Also, the hook now runs asynchronously.
* `padLoad`: Now runs when a temporary Pad object is created during import.
Also, it now runs asynchronously.
* `padRemove`: The `padID` context property is deprecated; use `pad.id`
instead.
* `padUpdate`: The `author` context property is deprecated; use the new
`authorId` context property instead. Also, the hook now runs asynchronously.
* Returning `true` from a `handleMessageSecurity` hook function is deprecated;
return `'permitOnce'` instead.
* Changes to the `src/static/js/Changeset.js` library:
* The following attribute processing functions are deprecated (use the new
attribute APIs instead):
* `attribsAttributeValue()`
* `eachAttribNumber()`
* `makeAttribsString()`
* `opAttributeValue()`
* `opIterator()`: Deprecated in favor of the new `deserializeOps()` generator
function.
* `appendATextToAssembler()`: Deprecated in favor of the new `opsFromAText()`
generator function.
* `newOp()`: Deprecated in favor of the new `Op` class.
* The `AuthorManager.getAuthor4Token()` function is deprecated; use the new
`AuthorManager.getAuthorId()` function instead.
* The exported database records covered by the `exportEtherpadAdditionalContent`
server-side hook now include keys like `${customPrefix}:${padId}:*`, not just
`${customPrefix}:${padId}`.
* Plugin locales should overwrite core's locales Stale
* Plugin locales overwrite core locales
# 1.8.18
Released: 2022-05-05
### Notable enhancements and fixes
* Upgraded ueberDB to fix a regression with CouchDB.
# 1.8.17
Released: 2022-02-23
### Security fixes
* Fixed a vunlerability in the `CHANGESET_REQ` message handler that allowed a
user with any access to read any pad if the pad ID is known.
### Notable enhancements and fixes
* Fixed a bug that caused all pad edit messages received at the server to go
through a single queue. Now there is a separate queue per pad as intended,
which should reduce message processing latency when many pads are active at
the same time.
# 1.8.16
### Security fixes
If you cannot upgrade to v1.8.16 for some reason, you are encouraged to try
cherry-picking the fixes to the version you are running:
```shell
git cherry-pick b7065eb9a0ec..77bcb507b30e
```
* Maliciously crafted `.etherpad` files can no longer overwrite arbitrary
non-pad database records when imported.
* Imported `.etherpad` files are now subject to numerous consistency checks
before any records are written to the database. This should help avoid
denial-of-service attacks via imports of malformed `.etherpad` files.
### Notable enhancements and fixes
* Fixed several `.etherpad` import bugs.
* Improved support for large `.etherpad` imports.
# 1.8.15
### Security fixes
* Fixed leak of the writable pad ID when exporting from the pad's read-only ID.
This only matters if you treat the writeable pad IDs as secret (e.g., you are
not using [ep_padlist2](https://www.npmjs.com/package/ep_padlist2)) and you
share the pad's read-only ID with untrusted users. Instead of treating
writeable pad IDs as secret, you are encouraged to take advantage of
Etherpad's authentication and authorization mechanisms (e.g., use
[ep_openid_connect](https://www.npmjs.com/package/ep_openid_connect) with
[ep_readonly_guest](https://www.npmjs.com/package/ep_readonly_guest), or write
your own
[authentication](https://etherpad.org/doc/v1.8.14/#index_authenticate) and
[authorization](https://etherpad.org/doc/v1.8.14/#index_authorize) plugins).
* Updated dependencies.
### Compatibility changes
* The `logconfig` setting is deprecated.
#### For plugin authors
* Etherpad now uses [jsdom](https://github.com/jsdom/jsdom) instead of
[cheerio](https://cheerio.js.org/) for processing HTML imports. There are two
consequences of this change:
* `require('ep_etherpad-lite/node_modules/cheerio')` no longer works. To fix,
your plugin should directly depend on `cheerio` and do `require('cheerio')`.
* The `collectContentImage` hook's `node` context property is now an
[`HTMLImageElement`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement)
object rather than a Cheerio Node-like object, so the API is slightly
different. See
[citizenos/ep_image_upload#49](https://github.com/citizenos/ep_image_upload/pull/49)
for an example fix.
* The `clientReady` server-side hook is deprecated; use the new `userJoin` hook
instead.
* The `init_<pluginName>` server-side hooks are now run every time Etherpad
starts up, not just the first time after the named plugin is installed.
* The `userLeave` server-side hook's context properties have changed:
* `auth`: Deprecated.
* `author`: Deprecated; use the new `authorId` property instead.
* `readonly`: Deprecated; use the new `readOnly` property instead.
* `rev`: Deprecated.
* Changes to the `src/static/js/Changeset.js` library:
* `opIterator()`: The unused start index parameter has been removed, as has
the unused `lastIndex()` method on the returned object.
* `smartOpAssembler()`: The returned object's `appendOpWithText()` method is
deprecated without a replacement available to plugins (if you need one, let
us know and we can make the private `opsFromText()` function public).
* Several functions that should have never been public are no longer exported:
`applyZip()`, `assert()`, `clearOp()`, `cloneOp()`, `copyOp()`, `error()`,
`followAttributes()`, `opString()`, `stringOp()`, `textLinesMutator()`,
`toBaseTen()`, `toSplices()`.
### Notable enhancements and fixes
* Accessibility fix for JAWS screen readers.
* Fixed "clear authorship" error (see issue #5128).
* Etherpad now considers square brackets to be valid URL characters.
* The server no longer crashes if an exception is thrown while processing a
message from a client.
* The `useMonospaceFontGlobal` setting now works (thanks @Lastpixl!).
* Chat improvements:
* The message input field is now a text area, allowing multi-line messages
(use shift-enter to insert a newline).
* Whitespace in chat messages is now preserved.
* Docker improvements:
* New `HEALTHCHECK` instruction (thanks @Gared!).
* New `settings.json` variables: `DB_COLLECTION`, `DB_URL`,
`SOCKETIO_MAX_HTTP_BUFFER_SIZE`, `DUMP_ON_UNCLEAN_EXIT` (thanks
@JustAnotherArchivist!).
* `.ep_initialized` files are no longer created.
* Worked around a [Firefox Content Security Policy
bug](https://bugzilla.mozilla.org/show_bug.cgi?id=1721296) that caused CSP
failures when `'self'` was in the CSP header. See issue #4975 for details.
* UeberDB upgraded from v1.4.10 to v1.4.18. For details, see the [ueberDB
changelog](https://github.com/ether/ueberDB/blob/master/CHANGELOG.md).
Highlights:
* The `postgrespool` driver was renamed to `postgres`, replacing the old
driver of that name. If you used the old `postgres` driver, you may see an
increase in the number of database connections.
* For `postgres`, you can now set the `dbSettings` value in `settings.json` to
a connection string (e.g., `"postgres://user:password@host/dbname"`) instead
of an object.
* For `mongodb`, the `dbName` setting was renamed to `database` (but `dbName`
still works for backwards compatibility) and is now optional (if unset, the
database name in `url` is used).
* `/admin/settings` now honors the `--settings` command-line argument.
* Fixed "Author *X* tried to submit changes as author *Y*" detection.
* Error message display improvements.
* Simplified pad reload after importing an `.etherpad` file.
#### For plugin authors
* `clientVars` was added to the context for the `postAceInit` client-side hook.
Plugins should use this instead of the `clientVars` global variable.
* New `userJoin` server-side hook.
* The `userLeave` server-side hook has a new `socket` context property.
* The `helper.aNewPad()` function (accessible to client-side tests) now
accepts hook functions to inject when opening a pad. This can be used to
test any new client-side hooks your plugin provides.
* Chat improvements:
* The `chatNewMessage` client-side hook context has new properties:
* `message`: Provides access to the raw message object so that plugins can
see the original unprocessed message text and any added metadata.
* `rendered`: Allows plugins to completely override how the message is
rendered in the UI.
* New `chatSendMessage` client-side hook that enables plugins to process the
text before sending it to the server or augment the message object with
custom metadata.
* New `chatNewMessage` server-side hook to process new chat messages before
they are saved to the database and relayed to users.
* Readability improvements to browser-side error stack traces.
* Added support for socket.io message acknowledgments.
# 1.8.14
### Security fixes
* Fixed a persistent XSS vulnerability in the Chat component. In case you can't
update to 1.8.14 directly, we strongly recommend to cherry-pick
a7968115581e20ef47a533e030f59f830486bdfa. Thanks to sonarsource for the
professional disclosure.
### Compatibility changes
* Node.js v12.13.0 or later is now required.
* The `favicon` setting is now interpreted as a pathname to a favicon file, not
a URL. Please see the documentation comment in `settings.json.template`.
* The undocumented `faviconPad` and `faviconTimeslider` settings have been
removed.
* MySQL/MariaDB now uses connection pooling, which means you will see up to 10
connections to the MySQL/MariaDB server (by default) instead of 1. This might
cause Etherpad to crash with a "ER_CON_COUNT_ERROR: Too many connections"
error if your server is configured with a low connection limit.
* Changes to environment variable substitution in `settings.json` (see the
documentation comments in `settings.json.template` for details):
* An environment variable set to the string "null" now becomes `null` instead
of the string "null". Similarly, if the environment variable is unset and
the default value is "null" (e.g., `"${UNSET_VAR:null}"`), the value now
becomes `null` instead of the string "null". It is no longer possible to
produce the string "null" via environment variable substitution.
* An environment variable set to the string "undefined" now causes the setting
to be removed instead of set to the string "undefined". Similarly, if the
environment variable is unset and the default value is "undefined" (e.g.,
`"${UNSET_VAR:undefined}"`), the setting is now removed instead of set to
the string "undefined". It is no longer possible to produce the string
"undefined" via environment variable substitution.
* Support for unset variables without a default value is now deprecated.
Please change all instances of `"${FOO}"` in your `settings.json` to
`${FOO:null}` to keep the current behavior.
* The `DB_*` variable substitutions in `settings.json.docker` that previously
defaulted to `null` now default to "undefined".
* Calling `next` without argument when using `Changeset.opIterator` does always
return a new Op. See b9753dcc7156d8471a5aa5b6c9b85af47f630aa8 for details.
### Notable enhancements and fixes
* MySQL/MariaDB now uses connection pooling, which should improve stability and
reduce latency.
* Bulk database writes are now retried individually on write failure.
* Minify: Avoid crash due to unhandled Promise rejection if stat fails.
* padIds are now included in /socket.io query string, e.g.
`https://video.etherpad.com/socket.io/?padId=AWESOME&EIO=3&transport=websocket&t=...&sid=...`.
This is useful for directing pads to separate socket.io nodes.
* <script> elements added via aceInitInnerdocbodyHead hook are now executed.
* Fix read only pad access with authentication.
* Await more db writes.
* Disabled wtfnode dump by default.
* Send `USER_NEWINFO` messages on reconnect.
* Fixed loading in a hidden iframe.
* Fixed a race condition with composition. (Thanks @ingoncalves for an
exceptionally detailed analysis and @rhansen for the fix.)
# 1.8.13
### Notable fixes
* Fixed a bug in the safeRun.sh script (#4935)
* Add more endpoints that do not need authentication/authorization (#4921)
* Fixed issue with non-opening device keyboard on smartphones (#4929)
* Add version string to iframe_editor.css to prevent stale cache entry (#4964)
### Notable enhancements
* Refactor pad loading (no document.write anymore) (#4960)
* Improve import/export functionality, logging and tests (#4957)
* Refactor CSS manager creation (#4963)
* Better metrics
* Add test for client height (#4965)
### Dependencies
* ueberDB2 1.3.2 -> 1.4.4
* express-rate-limit 5.2.5 -> 5.2.6
* etherpad-require-kernel 1.0.9 -> 1.0.11
# 1.8.12
Special mention: Thanks to Sauce Labs for additional testing tunnels to help us
grow! :)
### Security patches
* Fixed a regression in v1.8.11 which caused some pad names to cause Etherpad to
restart.
### Notable fixes
* Fixed a bug in the `dirty` database driver that sometimes caused Node.js to
crash during shutdown and lose buffered database writes.
* Fixed a regression in v1.8.8 that caused "Uncaught TypeError: Cannot read
property '0' of undefined" with some plugins (#4885)
* Less warnings in server console for supported element types on import.
* Support Azure and other network share installations by using a more truthful
relative path.
### Notable enhancements
* Dependency updates
* Various Docker deployment improvements
* Various new translations
* Improvement of rendering of plugin hook list and error message handling
# 1.8.11
### Notable fixes
* Fix server crash issue within PadMessageHandler due to SocketIO handling
* Fix editor issue with drop downs not being visible
* Ensure correct version is passed when loading front end resources
* Ensure underscore and jquery are available in original location for plugin comptability
### Notable enhancements
* Improved page load speeds
# 1.8.10
### Security Patches
* Resolve potential ReDoS vulnerability in your project - GHSL-2020-359
### Compatibility changes
* JSONP API has been removed in favor of using the mature OpenAPI implementation.
* Node 14 is now required for Docker Deployments
### Notable fixes
* Various performance and stability fixes
### Notable enhancements
* Improved line number alignment and user experience around line anchors
* Notification to admin console if a plugin is missing during user file import
* Beautiful loading and reconnecting animation
* Additional code quality improvements
* Dependency updates
# 1.8.9
### Notable fixes
* Fixed HTTP 400 error when importing via the UI.
* Fixed "Error: spawn npm ENOENT" crash on startup in Windows.
### Notable enhancements
* Removed some unnecessary arrow key handling logic.
* Dependency updates.
# 1.8.8
### Security patches
* EJS has been updated to 3.1.6 to mitigate an Arbitrary Code Injection
### Compatibility changes
* Node.js 10.17.0 or newer is now required.
* The `bin/` and `tests/` directories were moved under `src/`. Symlinks were
added at the old locations to hopefully avoid breaking user scripts and other
tools.
* Dependencies are now installed with the `--no-optional` flag to speed
installation. Optional dependencies such as `sqlite3` must now be manually
installed (e.g., `(cd src && npm i sqlite3)`).
* Socket.IO messages are now limited to 10K bytes to make denial of service
attacks more difficult. This may cause issues when pasting large amounts of
text or with plugins that send large messages (e.g., `ep_image_upload`). You
can change the limit via `settings.json`; see `socketIo.maxHttpBufferSize`.
* The top-level `package.json` file, added in v1.8.7, has been removed due to
problematic npm behavior. Whenever you install a plugin you will see the
following benign warnings that can be safely ignored:
```
npm WARN saveError ENOENT: no such file or directory, open '.../package.json'
npm WARN enoent ENOENT: no such file or directory, open '.../package.json'
npm WARN develop No description
npm WARN develop No repository field.
npm WARN develop No README data
npm WARN develop No license field.
```
### Notable enhancements
* You can now generate a link to a specific line number in a pad. Appending
`#L10` to a pad URL will cause your browser to scroll down to line 10.
* Database performance is significantly improved.
* Admin UI now has test coverage in CI. (The tests are not enabled by default;
see `settings.json`.)
* New stats/metrics: `activePads`, `httpStartTime`, `lastDisconnected`,
`memoryUsageHeap`.
* Improved import UX.
* Browser caching improvements.
* Users can now pick absolute white (`#fff`) as their color.
* The `settings.json` template used for Docker images has new variables for
controlling rate limiting.
* Admin UI now has test coverage in CI. (The tests are not enabled by default
because the admin password is required; see `settings.json`.)
* For plugin authors:
* New `callAllSerial()` function that invokes hook functions like `callAll()`
except it supports asynchronous hook functions.
* `callFirst()` and `aCallFirst()` now support the same wide range of hook
function behaviors that `callAll()`, `aCallAll()`, and `callAllSerial()`
support. Also, they now warn when a hook function misbehaves.
* The following server-side hooks now support asynchronous hook functions:
`expressConfigure`, `expressCreateServer`, `padCopy`, `padRemove`
* Backend tests for plugins can now use the
[`ep_etherpad-lite/tests/backend/common`](src/tests/backend/common.js)
module to start the server and simplify API access.
* The `checkPlugins.js` script now automatically adds GitHub CI test coverage
badges for backend tests and npm publish.
### Notable fixes
* Enter key now stays in focus when inserted at bottom of viewport.
* Numbering for ordered list items now properly increments when exported to
text.
* Suppressed benign socket.io connection errors
* Interface no longer loses color variants on disconnect/reconnect event.
* General code quality is further significantly improved.
* Restarting Etherpad via `/admin` actions is more robust.
* Improved reliability of server shutdown and restart.
* No longer error if no buttons are visible.
* For plugin authors:
* Fixed `collectContentLineText` return value handling.
# 1.8.7
### Compatibility-breaking changes
* **IMPORTANT:** It is no longer possible to protect a group pad with a
password. All API calls to `setPassword` or `isPasswordProtected` will fail.
Existing group pads that were previously password protected will no longer be
password protected. If you need fine-grained access control, you can restrict
API session creation in your frontend service, or you can use plugins.
* All workarounds for Microsoft Internet Explorer have been removed. IE might
still work, but it is untested.
* Plugin hook functions are now subject to new sanity checks. Buggy hook
functions will cause an error message to be logged
* Authorization failures now return 403 by default instead of 401
* The `authorize` hook is now only called after successful authentication. Use
the new `preAuthorize` hook if you need to bypass authentication
* The `authFailure` hook is deprecated; use the new `authnFailure` and
`authzFailure` hooks instead
* The `indexCustomInlineScripts` hook was removed
* The `client` context property for the `handleMessage` and
`handleMessageSecurity` hooks has been renamed to `socket` (the old name is
still usable but deprecated)
* The `aceAttribClasses` hook functions are now called synchronously
* The format of `ENTER`, `CREATE`, and `LEAVE` log messages has changed
* Strings passed to `$.gritter.add()` are now expected to be plain text, not
HTML. Use jQuery or DOM objects if you need formatting
### Notable new features
* Users can now import without creating and editing the pad first
* Added a new `readOnly` user setting that makes it possible to create users in
`settings.json` that can read pads but not create or modify them
* Added a new `canCreate` user setting that makes it possible to create users in
`settings.json` that can modify pads but not create them
* The `authorize` hook now accepts `readOnly` to grant read-only access to a pad
* The `authorize` hook now accepts `modify` to grant modify-only (creation
prohibited) access to a pad
* All authentication successes and failures are now logged
* Added a new `cookie.sameSite` setting that makes it possible to enable
authentication when Etherpad is embedded in an iframe from another site
* New `exportHTMLAdditionalContent` hook to include additional HTML content
* New `exportEtherpadAdditionalContent` hook to include additional database
content in `.etherpad` exports
* New `expressCloseServer` hook to close Express when required
* The `padUpdate` hook context now includes `revs` and `changeset`
* `checkPlugin.js` has various improvements to help plugin developers
* The HTTP request object (and therefore the express-session state) is now
accessible from within most `eejsBlock_*` hooks
* Users without a `password` or `hash` property in `settings.json` are no longer
ignored, so they can now be used by authentication plugins
* New permission denied modal and block ``permissionDenied``
* Plugins are now updated to the latest version instead of minor or patches
### Notable fixes
* Fixed rate limit accounting when Etherpad is behind a reverse proxy
* Fixed typos that prevented access to pads via an HTTP API session
* Fixed authorization failures for pad URLs containing a percent-encoded
character
* Fixed exporting of read-only pads
* Passwords are no longer written to connection state database entries or logged
in debug logs
* When using the keyboard to navigate through the toolbar buttons the button
with the focus is now highlighted
* Fixed support for Node.js 10 by passing the `--experimental-worker` flag
* Fixed export of HTML attributes within a line
* Fixed occasional "Cannot read property 'offsetTop' of undefined" error in
timeslider when "follow pad contents" is checked
* socket.io errors are now displayed instead of silently ignored
* Pasting while the caret is in a link now works (except for middle-click paste
on X11 systems)
* Removal of Microsoft Internet Explorer specific code
* Import better handles line breaks and white space
* Fix issue with ``createDiffHTML`` incorrect call of ``getInternalRevisionAText``
* Allow additional characters in URLs
* MySQL engine fix and various other UeberDB updates (See UeberDB changelog).
* Admin UI improvements on search results (to remove duplicate items)
* Removal of unused cruft from ``clientVars`` (``ip`` and ``userAgent``)
### Minor changes
* Temporary disconnections no longer force a full page refresh
* Toolbar layout for narrow screens is improved
* Fixed `SameSite` cookie attribute for the `language`, `token`, and `pref`
cookies
* Fixed superfluous database accesses when deleting a pad
* Expanded test coverage.
* `package-lock.json` is now lint checked on commit
* Various lint fixes/modernization of code
# 1.8.6
* IMPORTANT: This fixes a severe problem with postgresql in 1.8.5
* SECURITY: Fix authentication and authorization bypass vulnerabilities
* API: Update version to 1.2.15
* FEATURE: Add copyPadWithoutHistory API (#4295)
* FEATURE: Package more asset files to save http requests (#4286)
* MINOR: Improve UI when reconnecting
* TESTS: Improve tests
# 1.8.5
* IMPORTANT DROP OF SUPPORT: Drop support for IE. Browsers now need async/await.
* IMPORTANT SECURITY: Rate limit Commits when env=production
* SECURITY: Non completed uploads no longer crash Etherpad
* SECURITY: Log authentication requests
* FEATURE: Support ES6 (migrate from Uglify-JS to Terser)
* FEATURE: Improve support for non-cookie enabled browsers
* FEATURE: New hooks for ``index.html``
* FEATURE: New script to delete sessions.
* FEATURE: New setting to allow import withing an author session on a pad
* FEATURE: Checks Etherpad version on startup and notifies if update is available. Also available in ``/admin`` interface.
* FEATURE: Timeslider updates pad location to most recent edit
* MINOR: Outdent UL/LI items on removal of list item
* MINOR: Various UL/LI import/export bugs
* MINOR: PDF export fix
* MINOR: Front end tests no longer run (and subsequently error) on pull requests
* MINOR: Fix issue with </li> closing a list before it opens
* MINOR: Fix bug where large pads would fire a console error in timeslider
* MINOR: Fix ?showChat URL param issue
* MINOR: Issue where timeslider URI fails to be correct if padID is numeric
* MINOR: Include prompt for clear authorship when entire document is selected
* MINOR: Include full document aText every 100 revisions to make pad restoration on database corruption achievable
* MINOR: Several Colibris CSS fixes
* MINOR: Use mime library for mime types instead of hard-coded.
* MINOR: Don't show "new pad button" if instance is read only
* MINOR: Use latest NodeJS when doing Windows build
* MINOR: Change disconnect logic to reconnect instead of silently failing
* MINOR: Update SocketIO, async, jQuery and Mocha which were stuck due to stale code.
* MINOR: Rewrite the majority of the ``bin`` scripts to use more modern syntax
* MINOR: Improved CSS anomation through prefers-reduced-motion
* PERFORMANCE: Use workers (where possible) to minify CSS/JS on first page request. This improves initial startup times.
* PERFORMANCE: Cache EJS files improving page load speed when maxAge > 0.
* PERFORMANCE: Fix performance for large pads
* TESTS: Additional test coverage for OL/LI/Import/Export
* TESTS: Include Simulated Load Testing in CI.
* TESTS: Include content collector tests to test contentcollector.js logic external to pad dependents.
* TESTS: Include fuzzing import test.
* TESTS: Ensure CI is no longer using any cache
* TESTS: Fix various tests...
* TESTS: Various additional Travis testing including libreoffice import/export
# 1.8.4
* FIX: fix a performance regression on MySQL introduced in 1.8.3
* FIX: when running behind a reverse proxy and exposed in an inner directory, fonts and toolbar icons should now be visible. This is a regression introduced in 1.8.3
* FIX: cleanups in the UI after the CSS rehaul of 1.8.3
* MINOR: protect against bugged/stale UI elements after updates. An explicit cache busting via random query string is performed at each start. This needs to be replaced with hashed names in static assets.
* MINOR: improved some tests
* MINOR: fixed long-standing bugs in the maintenance tools in /bin (migrateDirtyDBtoRealDB, rebuildPad, convert, importSqlFile)
# 1.8.3
* FEATURE: colibris is now the default skin for new installs
* FEATURE: improved colibris visuals, and migrated to Flexbox layout
* FEATURE: skin variants: colibris skin colors can be easily customized. Visit http://127.0.0.1:9001/p/test#skinvariantsbuilder
* REQUIREMENTS: minimum required Node version is **10.13.0 LTS**.
* MINOR: stability fixes for the async migration in 1.8.0 (fixed many UnhandledPromiseRejectionWarning and the few remaining crashes)
* MINOR: improved stability of import/export functionality
* MINOR: fixed many small UI quirks (timeslider, import/export, chat)
* MINOR: Docker images are now built & run in production mode by default
* MINOR: reduced the size of the Docker images
* MINOR: better documented cookies and configuration parameters of the Docker image
* MINOR: better database support (especially MySQL)
* MINOR: additional test coverage
* MINOR: restored compatibility with ep_hash_auth
* MINOR: migrate from swagger-node-express to openapi-backend
* MINOR: honor the Accept-Language HTTP headers sent by browsers, eventually serving language variants
* PERFORMANCE: correctly send HTTP/304 for minified files
* SECURITY: bumped many dependencies. At the time of the release, this version has 0 reported vulnerabilities by npm audit
* SECURITY: never send referrer when opening a link
* SECURITY: rate limit imports and exports
* SECURITY: do not allow pad import if a user never contributed to that pad
* SECURITY: expose configuration parameter for limiting max import size
*BREAKING CHANGE*: undoing the "clear authorship colors" command is no longer supported (see https://github.com/ether/etherpad-lite/issues/2802)
*BREAKING CHANGE*: the visuals and CSS structure of the page was updated. Plugins may need a CSS rehaul
# 1.8
* SECURITY: change referrer policy so that Etherpad addresses aren't leaked when links are clicked (discussion: https://github.com/ether/etherpad-lite/pull/3636)
* SECURITY: set the "secure" flag for the session cookies when served over SSL. From now on it will not be possible to serve the same instance both in cleartext and over SSL
# 1.8-beta.1
* FEATURE: code was migrated to `async`/`await`, getting rid of a lot of callbacks (see https://github.com/ether/etherpad-lite/issues/3540)
* FEATURE: support configuration via environment variables
* FEATURE: include an official Dockerfile in the main repository
* FEATURE: support including plugins in custom Docker builds
* FEATURE: conditional creation of users: when its password is null, a user is not created. This helps, for example, in advanced configuration of Docker images.
* REQUIREMENTS: minimum required Node version is **8.9.0 LTS**. Release 1.8.3 will require at least Node **10.13.0** LTS
* MINOR: in the HTTP API, allow URL parameters and POST bodies to co-exist
* MINOR: fix Unicode bug in HTML export
* MINOR: bugfixes to colibris chat window
* MINOR: code simplification (avoided double negations, introduced early exits, ...)
* MINOR: reduced the size of the Windows package
* MINOR: upgraded the nodejs runtime to 10.16.3 in the Windows package
* SECURITY: avoided XSS in IE11
* SECURITY: the version is exposed in http header only when configured
* SECURITY: updated vendored jQuery version
* SECURITY: bumped dependencies
# 1.7.5
* FEATURE: introduced support for multiple skins. See https://etherpad.org/doc/v1.7.5/#index_skins
* FEATURE: added a new, optional skin. It can be activated choosing `skinName: "colibris"` in `settings.json`
* FEATURE: allow file import using LibreOffice
* SECURITY: updated many dependencies. No known high or moderate risk dependencies remain.
* SECURITY: generate better random pad names
* FIX: don't nuke all installed plugins if `npm install` fails
* FIX: improved LibreOffice export
* FIX: allow debug mode on node versions >= 6.3
* MINOR: started making Etherpad less dependent on current working directory when running
* MINOR: started simplifying the code structure, flattening complex conditions
* MINOR: simplified a bit the startup scripts
*UPGRADE NOTES*: if you have custom files in `src/static/custom`, save them
somewhere else, revert the directory contents, update to Etherpad 1.7.5, and
finally put them back in their new location, uder `src/static/skins/no-skin`.
# 1.7.0
* FIX: `getLineHTMLForExport()` no longer produces multiple copies of a line. **WARNING**: this could potentially break some plugins
* FIX: authorship of bullet points no longer changes when a second author edits them
* FIX: improved Firefox compatibility (non printable keys)
* FIX: `getPadPlainText()` was not working
* REQUIREMENTS: minimum required Node version is 6.9.0 LTS. The next release will require at least Node 8.9.0 LTS
* SECURITY: updated MySQL, Elasticsearch and PostgreSQL drivers
* SECURITY: started updating deprecated code and packages
* DOCS: documented --credentials, --apikey, --sessionkey. Better detailed contributors guidelines. Added a section on securing the installation
# 1.6.6
* FIX: line numbers are aligned with text again (broken in 1.6.4)
* FIX: text entered between connection loss and reconnection was not saved
* FIX: diagnostic call failed when etherpad was exposed in a subdirectory
# 1.6.5
* SECURITY: Escape data when listing available plugins
* FIX: Fix typo in apicalls.js which prevented importing isValidJSONPName
* FIX: fixed plugin dependency issue
* FIX: Update iframe_editor.css
* FIX: unbreak Safari iOS line wrapping
# 1.6.4
* SECURITY: Access Control bypass on /admin - CVE-2018-9845
* SECURITY: Remote Code Execution through pad export - CVE-2018-9327
* SECURITY: Remote Code Execution through JSONP handling - CVE-2018-9326
* SECURITY: Pad data leak - CVE-2018-9325
* Fix: Admin redirect URL
* Fix: Various script Fixes
* Fix: Various CSS/Style/Layout fixes
* NEW: Improved Pad contents readability
* NEW: Hook: onAccessCheck
* NEW: SESSIONKEY and APIKey customizable path
* NEW: checkPads script
* NEW: Support "cluster mode"
# 1.6.3
* SECURITY: Update ejs
* SECURITY: xss vulnerability when reading window.location.href
* SECURITY: sanitize jsonp
* NEW: Catch SIGTERM for graceful shutdown
* NEW: Show actual applied text formatting for caret position
* NEW: Add settings to improve scrolling of viewport on line changes
# 1.6.2
* NEW: Added pad shortcut disabling feature
* NEW: Create option to automatically reconnect after a few seconds
* Update: socket.io to 1.7.3
* Update: l10n lib
* Update: request to 2.83.0
* Update: Node for windows to 8.9.0
* Fix: minification of code
# 1.6.1
* NEW: Hook aceRegisterNonScrollableEditEvents to register events that shouldn't scroll
* NEW: Added 'item' parameter to registerAceCommand Hook
* NEW: Added LibreJS support
* Fix: Crash on malformed export url
* Fix: Re-enable editor after user is reconnected to server
* Fix: minification
* Other: Added 'no-referrer' for all pads
* Other: Improved cookie security
* Other: Fixed compatibility with nodejs 7
* Other: Updates
- socket.io to 1.6.0
- express to 4.13.4
- express-session to 1.13.0
- clean-css to 3.4.12
- uglify-js to 2.6.2
- log4js to 0.6.35
- cheerio to 0.20.0
- ejs to 2.4.1
- graceful-fs to 4.1.3
- semver to 5.1.0
- unorm to 1.4.1
- jsonminify to 0.4.1
- measured to 1.1.0
- mocha to 2.4.5
- supertest to 1.2.0
- npm to 4.0.2
- Node.js for Windows to 6.9.2
# 1.6.0
* SECURITY: Fix a possible xss attack in iframe link
* NEW: Add a aceSelectionChanged hook to allow plugins to react when the cursor location changes.
* NEW: Accepting Arrays on 'exportHtmlAdditionalTags' to handle attributes stored as ['key', 'value']
* NEW: Allow admin to run on a sub-directory
* NEW: Support version 5 of node.js
* NEW: Update windows build to node version 4.4.3
* NEW: Create setting to control if a new line will be indented or not
* NEW: Add an appendText API
* NEW: Allow LibreOffice to be used when exporting a pad
* NEW: Create hook exportHtmlAdditionalTagsWithData
* NEW: Improve DB migration performance
* NEW: allow settings to be applied from the filesystem
* NEW: remove applySettings hook and allow credentials.json to be part of core
* NEW: Use exec to switch to node process
* NEW: Validate incoming color codes
* Fix: Avoid space removal when pasting text from word processor.
* Fix: Removing style that makes editor scroll to the top on iOS without any action from the user
* Fix: Fix API call appendChatMessage to send new message to all connected clients
* Fix: Timeslider "Return to pad" button
* Fix: Generating pad HTML with tags like <span data-TAG="VALUE"> instead of <TAG:VALUE>
* Fix: Get git commit hash even if the repo only points to a bare repo.
* Fix: Fix decode error if pad name contains special characters and is sanitized
* Fix: Fix handleClientMessage_USER_* payloads not containing user info
* Fix: Set language cookie on initial load
* Fix: Timeslider Not Translated
* Other: set charset for mysql connection in settings.json
* Other: Dropped support for io.js
* Other: Add support to store credentials in credentials.json
* Other: Support node version 4 or higher
* Other: Update uberDB to version 0.3.0
# 1.5.7
* NEW: Add support for intermediate CA certificates for ssl
* NEW: Provide a script to clean up before running etherpad
* NEW: Use ctrl+shift+1 to do a ordered list
* NEW: Show versions of plugins on startup
* NEW: Add author on padCreate and padUpdate hook
* Fix: switchToPad method
* Fix: Dead keys
* Fix: Preserve new lines in copy-pasted text
* Fix: Compatibility mode on IE
* Fix: Content Collector to get the class of the DOM-node
* Fix: Timeslider export links
* Fix: Double prompt on file upload
* Fix: setText() replaces the entire pad text
* Fix: Accessibility features on embedded pads
* Fix: Tidy HTML before abiword conversion
* Fix: Remove edit buttons in read-only view
* Fix: Disable user input in read-only view
* Fix: Pads end with a single newline, rather than two newlines
* Fix: Toolbar and chat for mobile devices
# 1.5.6
* Fix: Error on windows installations
# 1.5.5
* SECURITY: Also don't allow read files on directory traversal on minify paths
* NEW: padOptions can be set in settings.json now
* Fix: Add check for special characters in createPad API function
* Fix: Middle click on a link in firefox don't paste text anymore
* Fix: Made setPadRaw async to import larger etherpad files
* Fix: rtl
* Fix: Problem in older IEs
* Other: Update to express 4.x
* Other: Dropped support for node 0.8
* Other: Update ejs to version 2.x
* Other: Moved sessionKey from settings.json to a new auto-generated SESSIONKEY.txt file
# 1.5.4
* SECURITY: Also don't allow read files on directory traversal on frontend tests path
# 1.5.3
* NEW: Accessibility support for Screen readers, includes new fonts and keyboard shortcuts
* NEW: API endpoint for Append Chat Message and Chat Backend Tests
* NEW: Error messages displayed on load are included in Default Pad Text (can be suppressed)
* NEW: Content Collector can handle key values
* NEW: getAttributesOnPosition Method
* FIX: Firefox keeps attributes (bold etc) on cut/copy -> paste
* Fix: showControls=false now works
* Fix: Cut and Paste works...
* SECURITY: Don't allow read files on directory traversal
# 1.5.2
* NEW: Support for node version 0.12.x
* NEW: API endpoint saveRevision, getSavedRevisionCount and listSavedRevisions
* NEW: setting to allow load testing
* Fix: Rare scroll issue
* Fix: Handling of custom pad path
* Fix: Better error handling of imports and exports of type "etherpad"
* Fix: Walking caret in chrome
* Fix: Better handling for changeset problems
* SECURITY Fix: Information leak for etherpad exports (CVE-2015-2298)
# 1.5.1
* NEW: High resolution Icon
* NEW: Use HTTPS for plugins.json download
* NEW: Add 'last update' column
* NEW: Show users and chat at the same time
* NEW: Support io.js
* Fix: removeAttributeOnLine now works properly
* Fix: Plugin search and list
* Fix: Issue where unauthed request could cause error
* Fix: Privacy issue with .etherpad export
* Fix: Freeze deps to improve bisectability
* Fix: IE, everything. IE is so broken.
* Fix: Timeslider proxy
* Fix: All backend tests pass
* Fix: Better support for Export into HTML
* Fix: Timeslider stars
* Fix: Translation update
* Fix: Check filesystem if Abiword exists
* Fix: Docs formatting
* Fix: Move Save Revision notification to a gritter message
* Fix: UeberDB MySQL Timeout issue
* Fix: Indented +9 list items
* Fix: Don't paste on middle click of link
* SECURITY Fix: Issue where a malformed URL could cause EP to disclose installation location
# 1.5.0
* NEW: Lots of performance improvements for page load times
* NEW: Hook for adding CSS to Exports
* NEW: Allow shardable socket io
* NEW: Allow UI to show when attr/prop is applied (CSS)
* NEW: Various scripts
* NEW: Export full fidelity pads (including authors etc.)
* NEW: Various front end tests
* NEW: Backend tests
* NEW: switchPad hook to instantly switch between pads
* NEW: Various translations
* NEW: Icon sets instead of images to provide quality high DPI experience
* Fix: HTML Import blocking / hanging server
* Fix: Export Bullet / Numbered lists HTML
* Fix: Swagger deprecated warning
* Fix: Bad session from crashing server
* Fix: Allow relative settings path
* Fix: Stop attributes being improperly assigned between 2 lines
* Fix: Copy / Move Pad API race condition
* Fix: Save all user preferences
* Fix: Upgrade majority of dependency inc upgrade to SocketIO1+
* Fix: Provide UI button to restore maximized chat window
* Fix: Timeslider UI Fix
* Fix: Remove Dokuwiki
* Fix: Remove long paths from windows build (stops error during extract)
* Fix: Various globals removed
* Fix: Move all scripts into bin/
* Fix: Various CSS bugfixes for Mobile devices
* Fix: Overflow Toolbar
* Fix: Line Attribute management
# 1.4.1
* NEW: Translations
* NEW: userLeave Hook
* NEW: Script to reinsert all DB values of a Pad
* NEW: Allow for absolute settings paths
* NEW: API: Get Pad ID from read Only Pad ID
* NEW: Huge improvement on MySQL database read/write (InnoDB to MyISAM)
* NEW: Hook for Export File Name
* NEW: Preprocessor Hook for DOMLine attributes (allows plugins to wrap entire line contents)
* Fix: Exception on Plugin Search and fix for plugins not being fetched
* Fix: Font on innerdoc body can be arial on paste
* Fix: Fix Dropping of messages in handleMessage
* Fix: Don't use Abiword for HTML exports
* Fix: Color issues with user Icon
* Fix: Timeslider Button
* Fix: Session Deletion error
* Fix: Allow browser tabs to be cycled when focus is in editor
* Fix: Various Editor issues with Easysync potentially entering forever loop on bad changeset
# 1.4
* NEW: Disable toolbar items through settings.json
* NEW: Internal stats/metrics engine
* NEW: Copy/Move Pad API functions
* NEW: getAttributeOnSelection method
* NEW: CSS function when an attribute is active on caret location
* NEW: Various new eejs blocks
* NEW: Ace afterEditHook
* NEW: Import hook to introduce alternative export methods
* NEW: preProcessDomLine allows Domline attributes to be processed before native attributes
* Fix: Allow for lighter author colors
* Fix: Improved randomness of session tokens
* Fix: Don't panic if an author2session/group2session no longer exists
* Fix: Gracefully fallback to related languages if chosen language is unavailable
* Fix: Various changeset/stability bugs
* Fix: Re-enable import buttons after failed import
* Fix: Allow browser tabs to be cycled when in editor
* Fix: Better Protocol detection
* Fix: padList API Fix
* Fix: Caret walking issue
* Fix: Better settings.json parsing
* Fix: Improved import/export handling
* Other: Various whitespace/code clean-up
* Other: .deb packaging creator
* Other: More API Documentation
* Other: Lots more translations
* Other: Support Node 0.11
# 1.3
* NEW: We now follow the semantic versioning scheme!
* NEW: Option to disable IP logging
* NEW: Localisation updates from https://translatewiki.net.
* Fix: Fix readOnly group pads
* Fix: don't fetch padList on every request
# 1.2.12
* NEW: Add explanations for more disconnect scenarios
* NEW: export sessioninfos so plugins can access it
* NEW: pass pad in postAceInit hook
* NEW: Add trustProxy setting. ALlows to make ep use X-forwarded-for as remoteAddress
* NEW: userLeave hook (UNDOCUMENTED)
* NEW: Plural macro for translations
* NEW: backlinks to main page in Admin pages
* NEW: New translations from translatewiki.net
* SECURITY FIX: Filter author data sent to clients
* FIX: Never keep processing a changeset if it's corrupted
* FIX: Some client-side performance fixes for webkit browsers
* FIX: Only execute listAllPads query on demand (not on start-up)
* FIX: HTML import (don't crash on malformed or blank HTML input; strip title out of html during import)
* FIX: check if uploaded file only contains ascii chars when abiword disabled
* FIX: Plugin search in /admin/plugins
* FIX: Don't create new pad if a non-existent read-only pad is accessed
* FIX: Drop messages from unknown connections (would lead to a crash after a restart)
* FIX: API: fix createGroupFor endpoint, if mapped group is deleted
* FIX: Import form for other locales
* FIX: Don't stop processing changeset queue if there is an error
* FIX: Caret movement. Chrome detects blank rows line heights as incorrect
* FIX: allow colons in password
* FIX: Polish logging of client-side errors on the server
* FIX: Username url param
* FIX: Make start script POSIX ompatible
# 1.2.11
* NEW: New Hook for outer_ace dynamic css manager and author style hook
* NEW: Bump log4js for improved logging
* Fix: Remove URL schemes which don't have RFC standard
* Fix: Fix safeRun subsequent restarts issue
* Fix: Allow safeRun to pass arguments to run.sh
* Fix: Include script for more efficient import
* Fix: Fix sysv comptibile script
* Fix: Fix client side changeset spamming
* Fix: Don't crash on no-auth
* Fix: Fix some IE8 errors
* Fix: Fix authorship sanitation
# 1.2.10
* NEW: Broadcast slider is exposed in timeslider so plugins can interact with it
* Fix: IE issue where pads wouldn't load due to missing console from i18n
* Fix: console issue in collab client would error on cross domain embeds in IE
* Fix: Only Restart Etherpad once plugin is installed
* Fix: Only redraw lines that exist after drag and drop
* Fix: Pasting into ordered list
* Fix: Import browser detection
* Fix: 2 Part Locale Specs
* Fix: Remove language string from chat element
* Fix: Make Saved revision Star fade back out on non Top frames
* Other: Remove some cruft legacy JS from old Etherpad
* Other: Express 3.1.2 breaks sessions, set Express to 3.1.0
# 1.2.91
* NEW: Authors can now send custom object messages to other Authors making 3 way conversations possible. This introduces WebRTC plugin support.
* NEW: Hook for Chat Messages Allows for Desktop Notification support
* NEW: FreeBSD installation docs
* NEW: Ctrl S for save revision makes the Icon glow for a few sconds.
* NEW: Various hooks and expose the document ACE object
* NEW: Plugin page revamp makes finding and installing plugins more sane.
* NEW: Icon to enable sticky chat from the Chat box
* Fix: Cookies inside of plugins
* Fix: Don't leak event emitters when accessing admin/plugins
* Fix: Don't allow user to send messages after they have been "kicked" from a pad
* Fix: Refactor Caret navigation with Arrow and Pageup/down keys stops cursor being lost
* Fix: Long lines in Firefox now wrap properly
* Fix: Session Disconnect limit is increased from 10 to 20 to support slower restarts
* Fix: Support Node 0.10
* Fix: Log HTTP on DEBUG log level
* Fix: Server wont crash on import fails on 0 file import.
* Fix: Import no longer fails consistently
* Fix: Language support for non existing languages
* Fix: Mobile support for chat notifications are now usable
* Fix: Re-Enable Editbar buttons on reconnect
* Fix: Clearing authorship colors no longer disconnects all clients
* Other: New debug information for sessions
# 1.2.9
* Fix: MAJOR Security issue, where a hacker could submit content as another user
* Fix: security issue due to unescaped user input
* Fix: Admin page at /admin redirects to /admin/ now to prevent breaking relative links
* Fix: indentation in chrome on linux
* Fix: PadUsers API endpoint
* NEW: A script to import data to all dbms
* NEW: Add authorId to chat and userlist as a data attribute
* NEW: Refactor and fix our frontend tests
* NEW: Localisation updates
# 1.2.81
* Fix: CtrlZ-Y for Undo Redo
* Fix: RTL functionality on contents & fix RTL/LTR tests and RTL in Safari
* Fix: Various other tests fixed in Android
# 1.2.8
! IMPORTANT: New setting.json value is required to automatically reconnect clients on disconnect
* NEW: Use Socket IO for rooms (allows for pads to be load balanced with sticky rooms)
* NEW: Plugins can now provide their own frontend tests
* NEW: Improved server-side logging
* NEW: Admin dashboard mobile device support and new hooks for Admin dashboard
* NEW: Get current API version from API
* NEW: CLI script to delete pads
* Fix: Automatic client reconnection on disconnect
* Fix: Text Export indentation now supports multiple indentations
* Fix: Bugfix getChatHistory API method
* Fix: Stop Chrome losing caret after paste is texted
* Fix: Make colons on end of line create 4 spaces on indent
* Fix: Stop the client disconnecting if a rev is in the wrong order
* Fix: Various server crash issues based on rev in wrong order
* Fix: Various tests
* Fix: Make indent when on middle of the line stop creating list
* Fix: Stop long strings breaking the UX by moving focus away from beginning of line
* Fix: Redis findKeys support
* Fix: padUsersCount no longer hangs server
* Fix: Issue with two part locale specs not working
* Fix: Make plugin search case insensitive
* Fix: Indentation and bullets on text export
* Fix: Resolve various warnings on dependencies during install
* Fix: Page up / Page down now works in all browsers
* Fix: Stop Opera browser inserting two new lines on enter keypress
* Fix: Stop timeslider from showing NaN on pads with only one revision
* Other: Allow timeslider tests to run and provide & fix various other frontend-tests
* Other: Begin dropping reference to Lite. Etherpad Lite is now named "Etherpad"
* Other: Update to latest jQuery
* Other: Change loading message asking user to please wait on first build
* Other: Allow etherpad to use global npm installation (Safe since node 6.3)
* Other: Better documentation for log rotation and log message handling
# 1.2.7
* NEW: notifications are now modularized and can be stacked
* NEW: Visit a specific revision in the timeslider by suffixing #%revNumber% IE http://localhost/p/test/timeslider#12
* NEW: Link to plugin on Admin page allows admins to easily see plugin details in a new window by clicking on the plugin name
* NEW: Automatically see plugins that require update and be able to one click update
* NEW: API endpoints for Chat .. getChatHistory, getChatHead
* NEW: API endpoint to see a pad diff in HTML format from revision x to revision y .. createPadDiffHTML
* NEW: Real time plugin search & unified menu UI for admin pages
* Fix: MAJOR issue where server could be crashed by malformed client message
* Fix: AuthorID is now included in padUsers API response
* Fix: make docs
* Fix: Timeslider UI bug with slider not being in position
* Fix: IE8 language issue where it wouldn't load pads due to IE8 suckling on the bussum of hatrid
* Fix: Import timeout issue
* Fix: Import now works if Params are set in pad URL
* Fix: Convert script
* Other: Various new language strings and update/bugfixes of others
* Other: Clean up the getParams functionality
* Other: Various new EEJS blocks: index, timeslider, html etc.
# 1.2.6
* Fix: Package file UeberDB reference
* New #users EEJS block for plugins
# 1.2.5
* Create timeslider EEJS blocks for plugins
* Allow for "more messages" to be loaded in chat
* Introduce better logging
* API endpoint for "listAllPads"
* Fix: Stop highlight of timeslider when dragging mouse
* Fix: Time Delta on Timeslider make date update properly
* Fix: Prevent empty chat messages from being sent
* Fix: checkPad script
* Fix: IE onLoad listener for i18n
# 1.2.4
* Fix IE console issue created in 1.2.3
* Allow CI Tests to pass by ignoring timeslider test
* Fix broken placeholders in locales
* Fix extractPadData script
* Fix documentation for checkToken
* Fix hitting enter on form in admin/plugins
# 1.2.3
* Fix #1307: Chrome needs console.log to be called on console obj
* Fix #1309: We had broken support for node v0.6 in the last release
# 1.2.2
* More translations and better language support. See https://translatewiki.net/wiki/Translating:Etherpad_lite for more details
* Add a checkToken Method to the API
* Bugfix for Internal Caching issue that was causing some 404s on images.
* Bugfix for IE Import
* Bugfix for Node 0.6 compatibility
* Bugfix for multiple cookie support
* Bugfix for API when requireAuth is enabled.
* Plugin page now shows plugin version #
* Show color of Author in Chat messages
* Allow plugin search by description
* Allow for different socket IO transports
* Allow for custom favicon path
* Control S now does Create new Revision functionality
* Focus on password when required
* Frontend Timeslider test
* Allow for basic HTML etc. import without abiword
* Native HTTPS support
# 1.2.1
* Allow ! in urls inside the editor (Not Pad urls)
* Allow comments in language files
* More languages (Finish, Spanish, Bengali, Dutch) Thanks to TranslateWiki.net team. See https://translatewiki.net/w/i.php?title=Special:MessageGroupStats&group=out-etherpad-lite for more details
* Bugfix for IE7/8 issue with a JS error #1186
* Bugfix windows package extraction issue and make the .zip file smaller
* Bugfix group pad API export
* Kristen Stewart is a terrible actress and Twilight sucks.
# v1.2
* Internationalization / Language / Translation support (i18n) with support for German/French
* A frontend/client side testing framework and backend build tests
* Customizable robots.txt
* Customizable app title (finally you can name your epl instance!)
* eejs render arguments are now passed on to eejs hooks through the newly introduced `renderContext` argument.
* Plugin-specific settings in settings.json (finally allowing for things like a google analytics plugin)
* Serve admin dashboard at /admin (still very limited, though)
* Modify your settings.json through the newly created UI at /admin/settings
* Fix: Import `<ol>` as `<ol>` and not as `<ul>`!
* Added solaris compatibility (bin/installDeps.sh was broken on solaris)
* Fix a bug with IE9 and Password Protected Pads using HTTPS
# v1.1.5
* We updated to express v3 (please [make sure](https://github.com/visionmedia/express/wiki/Migrating-from-2.x-to-3.x) your plugin works under express v3)
* `userColor` URL parameter which sets the initial author color
* Hooks for "padCreate", "padRemove", "padUpdate" and "padLoad" events
* Security patches concerning the handling of messages originating from clients
* Our database abstraction layer now natively supports couchDB, levelDB, mongoDB, postgres, and redis!
* We now provide a script helping you to migrate from dirtyDB to MySQL
* Support running Etherpad Lite behind IIS, using [iisnode](https://github.com/tjanczuk/iisnode/wiki)
* LibreJS Licensing information in headers of HTML templates
* Default port number to PORT env var, if port isn't specified in settings
* Fix for `convert.js`
* Raise upper char limit in chat to 999 characters
* Fixes for mobile layout
* Fixes for usage behind reverse proxy
* Improved documentation
* Fixed some opera style bugs
* Update npm and fix some bugs, this introduces
# v1.1
* Introduced Plugin framework
* Many bugfixes
* Faster page loading
* Various UI polishes
* Saved Revisions
* Read only Real time view
* More API functionality
# v 1.0.1
* Updated MySQL driver, this fixes some problems with mysql
* Fixed export,import and timeslider link when embed parameters are used
================================================
FILE: CONTRIBUTING.md
================================================
# Contributor Guidelines
(Please talk to people on the mailing list before you change this page, see our section on [how to get in touch](https://github.com/ether/etherpad-lite#get-in-touch))
**We have decided that LLM/Agent/AI contributions are fine as long as they are within the instructions set out by this document.**
## Pull requests
* the commit series in the PR should be _linear_ (it **should not contain merge commits**). This is necessary because we want to be able to [bisect](https://en.wikipedia.org/wiki/Bisection_(software_engineering)) bugs easily. Rewrite history/perform a rebase if necessary
* PRs should be issued against the **develop** branch: we never pull directly into **master**
* PRs **should not have conflicts** with develop. If there are, please resolve them rebasing and force-pushing
* when preparing your PR, please make sure that you have included the relevant **changes to the documentation** (preferably with usage examples)
* contain meaningful and detailed **commit messages** in the form:
```
submodule: description
longer description of the change you have made, eventually mentioning the
number of the issue that is being fixed, in the form: Fixes #someIssueNumber
```
* if the PR is a **bug fix**:
* The commit that fixes the bug should **include a regression test** that
would fail if the bug fix was reverted. Adding the regression test in the
same commit as the bug fix makes it easier for a reviewer to verify that the
test is appropriate for the bug fix.
* If there is a bug report, **the pull request description should include the
text "`Fixes #xxx`"** so that the bug report is auto-closed when the PR is
merged. It is less useful to say the same thing in a commit message because
GitHub will spam the bug report every time the commit is rebased, and
because a bug number alone becomes meaningless in forks. (A full URL would
be better, but ideally each commit is readable on its own without the need
to examine an external reference to understand motivation or context.)
* think about stability: code has to be backwards compatible as much as possible. Always **assume your code will be run with an older version of the DB/config file**
* if you want to remove a feature, **deprecate it instead**:
* write an issue with your deprecation plan
* output a `WARN` in the log informing that the feature is going to be removed
* remove the feature in the next version
* if you want to add a new feature, put it under a **feature flag**:
* once the new feature has reached a minimal level of stability, do a PR for it, so it can be integrated early
* expose a mechanism for enabling/disabling the feature
* the new feature should be **disabled** by default. With the feature disabled, the code path should be exactly the same as before your contribution. This is a __necessary condition__ for early integration
* think of the PR not as something that __you wrote__, but as something that __someone else is going to read__. The commit series in the PR should tell a novice developer the story of your thoughts when developing it
## How to write a bug report
* Please be polite, we all are humans and problems can occur.
* Please add as much information as possible, for example
* client os(s) and version(s)
* browser(s) and version(s), is the problem reproducible on different clients
* special environments like firewalls or antivirus
* host os and version
* npm and nodejs version
* Logfiles if available
* steps to reproduce
* what you expected to happen
* what actually happened
* Please format logfiles and code examples with markdown see github Markdown help below the issue textarea for more information.
If you send logfiles, please set the loglevel switch DEBUG in your settings.json file:
```
/* The log level we are using, can be: DEBUG, INFO, WARN, ERROR */
"loglevel": "DEBUG",
```
The logfile location is defined in startup script or the log is directly shown in the commandline after you have started etherpad.
## General goals of Etherpad
To make sure everybody is going in the same direction:
* easy to install for admins and easy to use for people
* easy to integrate into other apps, but also usable as standalone
* lightweight and scalable
* extensible, as much functionality should be extendable with plugins so changes don't have to be done in core.
Also, keep it maintainable. We don't wanna end up as the monster Etherpad was!
## How to work with git?
* Don't work in your master branch.
* Make a new branch for every feature you're working on. (This ensures that you can work you can do lots of small, independent pull requests instead of one big one with complete different features)
* Don't use the online edit function of github (this only creates ugly and not working commits!)
* Try to make clean commits that are easy readable (including descriptive commit messages!)
* Test before you push. Sounds easy, it isn't!
* Don't check in stuff that gets generated during build or runtime
* Make small pull requests that are easy to review but make sure they do add value by themselves / individually
## Coding style
* Do write comments. (You don't have to comment every line, but if you come up with something that's a bit complex/weird, just leave a comment. Bear in mind that you will probably leave the project at some point and that other people will read your code. Undocumented huge amounts of code are worthless!)
* Never ever use tabs
* Indentation: 2 spaces
* Don't overengineer. Don't try to solve any possible problem in one step, but try to solve problems as easy as possible and improve the solution over time!
* Do generalize sooner or later! (if an old solution, quickly hacked together, poses more problems than it solves today, refactor it!)
* Keep it compatible. Do not introduce changes to the public API, db schema or configurations too lightly. Don't make incompatible changes without good reasons!
* If you do make changes, document them! (see below)
* Use protocol independent urls "//"
## Branching model / git workflow
see git flow http://nvie.com/posts/a-successful-git-branching-model/
### `master` branch
* the stable
* This is the branch everyone should use for production stuff
### `develop`branch
* everything that is READY to go into master at some point in time
* This stuff is tested and ready to go out
### release branches
* stuff that should go into master very soon
* only bugfixes go into these (see http://nvie.com/posts/a-successful-git-branching-model/ for why)
* we should not be blocking new features to develop, just because we feel that we should be releasing it to master soon. This is the situation that release branches solve/handle.
### hotfix branches
* fixes for bugs in master
### feature branches (in your own repos)
* these are the branches where you develop your features in
* If it's ready to go out, it will be merged into develop
Over the time we pull features from feature branches into the develop branch. Every month we pull from develop into master. Bugs in master get fixed in hotfix branches. These branches will get merged into master AND develop. There should never be commits in master that aren't in develop
## Documentation
The docs are in the `doc/` folder in the git repository, so people can easily find the suitable docs for the current git revision.
Documentation should be kept up-to-date. This means, whenever you add a new API method, add a new hook or change the database model, pack the relevant changes to the docs in the same pull request.
You can build the docs e.g. produce html, using `make docs`. At some point in the future we will provide an online documentation. The current documentation in the github wiki should always reflect the state of `master` (!), since there are no docs in master, yet.
## Testing
Front-end tests are found in the `tests/frontend/` folder in the repository. Run them by pointing your browser to `<yourdomainhere>/tests/frontend`.
Back-end tests can be run from the `src` directory, via `npm test`.
You can use `npm test -- --inspect-brk` and navigate to `edge://inspect` or `chrome://inspect` to debug the tests.
## Things you can help with
Etherpad is much more than software. So if you aren't a developer then worry not, there is still a LOT you can do! A big part of what we do is community engagement. You can help in the following ways
* Triage bugs (applying labels) and confirming their existence
* Testing fixes (simply applying them and seeing if it fixes your issue or not) - Some git experience required
* Notifying large site admins of new releases
* Writing Changelogs for releases
* Creating Windows packages
* Creating releases
* Bumping dependencies periodically and checking they don't break anything
* Write proposals for grants
* Co-Author and Publish CVEs
* Work with SFC to maintain legal side of project
* Maintain TODO page - https://github.com/ether/etherpad-lite/wiki/TODO#IMPORTANT_TODOS
================================================
FILE: Dockerfile
================================================
# Etherpad Lite Dockerfile
#
# https://github.com/ether/etherpad-lite
#
# Author: muxator
ARG BUILD_ENV=git
ARG PnpmVersion=10.28.2
FROM node:lts-alpine AS adminbuild
RUN npm install -g pnpm@${PnpmVersion}
WORKDIR /opt/etherpad-lite
COPY . .
RUN pnpm install
RUN pnpm run build:ui
FROM node:lts-alpine AS build
LABEL maintainer="Etherpad team, https://github.com/ether/etherpad-lite"
# Set these arguments when building the image from behind a proxy
ARG http_proxy=
ARG https_proxy=
ARG no_proxy=
ARG TIMEZONE=
RUN \
[ -z "${TIMEZONE}" ] || { \
apk add --no-cache tzdata && \
cp /usr/share/zoneinfo/${TIMEZONE} /etc/localtime && \
echo "${TIMEZONE}" > /etc/timezone; \
}
ENV TIMEZONE=${TIMEZONE}
# Control the configuration file to be copied into the container.
ARG SETTINGS=./settings.json.docker
# plugins to install while building the container. By default no plugins are
# installed.
# If given a value, it has to be a space-separated, quoted list of plugin names.
#
# EXAMPLE:
# ETHERPAD_PLUGINS="ep_codepad ep_author_neat"
ARG ETHERPAD_PLUGINS=
# local plugins to install while building the container. By default no plugins are
# installed.
# If given a value, it has to be a space-separated, quoted list of plugin names.
#
# EXAMPLE:
# ETHERPAD_LOCAL_PLUGINS="../ep_my_plugin ../ep_another_plugin"
ARG ETHERPAD_LOCAL_PLUGINS=
# github plugins to install while building the container. By default no plugins are
# installed.
# If given a value, it has to be a space-separated, quoted list of plugin names.
#
# EXAMPLE:
# ETHERPAD_GITHUB_PLUGINS="ether/ep_plugin"
ARG ETHERPAD_GITHUB_PLUGINS=
# Control whether abiword will be installed, enabling exports to DOC/PDF/ODT formats.
# By default, it is not installed.
# If given any value, abiword will be installed.
#
# EXAMPLE:
# INSTALL_ABIWORD=true
ARG INSTALL_ABIWORD=
# Control whether libreoffice will be installed, enabling exports to DOC/PDF/ODT formats.
# By default, it is not installed.
# If given any value, libreoffice will be installed.
#
# EXAMPLE:
# INSTALL_LIBREOFFICE=true
ARG INSTALL_SOFFICE=
# Install dependencies required for modifying access.
RUN apk add --no-cache shadow bash
# Follow the principle of least privilege: run as unprivileged user.
#
# Running as non-root enables running this image in platforms like OpenShift
# that do not allow images running as root.
#
# If any of the following args are set to the empty string, default
# values will be chosen.
ARG EP_HOME=
ARG EP_UID=5001
ARG EP_GID=0
ARG EP_SHELL=
RUN groupadd --system ${EP_GID:+--gid "${EP_GID}" --non-unique} etherpad && \
useradd --system ${EP_UID:+--uid "${EP_UID}" --non-unique} --gid etherpad \
${EP_HOME:+--home-dir "${EP_HOME}"} --create-home \
${EP_SHELL:+--shell "${EP_SHELL}"} etherpad
ARG EP_DIR=/opt/etherpad-lite
RUN mkdir -p "${EP_DIR}" && chown etherpad:etherpad "${EP_DIR}"
# the mkdir is needed for configuration of openjdk-11-jre-headless, see
# https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=863199
RUN \
mkdir -p /usr/share/man/man1 && \
npm install pnpm@${PnpmVersion} -g && \
apk update && apk upgrade && \
apk add --no-cache \
ca-certificates \
curl \
git \
${INSTALL_ABIWORD:+abiword abiword-plugin-command} \
${INSTALL_SOFFICE:+libreoffice openjdk8-jre libreoffice-common} && \
rm -rf /var/cache/apk/*
USER etherpad
WORKDIR "${EP_DIR}"
# etherpads version feature requires this. Only copy what is really needed
COPY --chown=etherpad:etherpad ${SETTINGS} ./settings.json
COPY --chown=etherpad:etherpad ./var ./var
COPY --chown=etherpad:etherpad ./bin ./bin
COPY --chown=etherpad:etherpad ./pnpm-workspace.yaml ./package.json ./
FROM build AS build_git
ONBUILD COPY --chown=etherpad:etherpad ./.git/HEA[D] ./.git/HEAD
ONBUILD COPY --chown=etherpad:etherpad ./.git/ref[s] ./.git/refs
FROM build AS build_copy
FROM build_${BUILD_ENV} AS development
ARG ETHERPAD_PLUGINS=
ARG ETHERPAD_LOCAL_PLUGINS=
ARG ETHERPAD_LOCAL_PLUGINS_ENV=
ARG ETHERPAD_GITHUB_PLUGINS=
COPY --chown=etherpad:etherpad ./src/ ./src/
COPY --chown=etherpad:etherpad --from=adminbuild /opt/etherpad-lite/src/templates/admin ./src/templates/admin
COPY --chown=etherpad:etherpad --from=adminbuild /opt/etherpad-lite/src/static/oidc ./src/static/oidc
COPY --chown=etherpad:etherpad ./local_plugin[s] ./local_plugins/
RUN bash -c ./bin/installLocalPlugins.sh
RUN bin/installDeps.sh && \
if [ ! -z "${ETHERPAD_PLUGINS}" ] || [ ! -z "${ETHERPAD_GITHUB_PLUGINS}" ]; then \
pnpm run plugins i ${ETHERPAD_PLUGINS} ${ETHERPAD_GITHUB_PLUGINS:+--github ${ETHERPAD_GITHUB_PLUGINS}}; \
fi
FROM build_${BUILD_ENV} AS production
ARG ETHERPAD_PLUGINS=
ARG ETHERPAD_LOCAL_PLUGINS=
ARG ETHERPAD_LOCAL_PLUGINS_ENV=
ARG ETHERPAD_GITHUB_PLUGINS=
ENV NODE_ENV=production
ENV ETHERPAD_PRODUCTION=true
COPY --chown=etherpad:etherpad ./src ./src
COPY --chown=etherpad:etherpad --from=adminbuild /opt/etherpad-lite/src/templates/admin ./src/templates/admin
COPY --chown=etherpad:etherpad --from=adminbuild /opt/etherpad-lite/src/static/oidc ./src/static/oidc
COPY --chown=etherpad:etherpad ./local_plugin[s] ./local_plugins/
RUN bash -c ./bin/installLocalPlugins.sh
RUN bin/installDeps.sh && \
if [ ! -z "${ETHERPAD_PLUGINS}" ] || [ ! -z "${ETHERPAD_GITHUB_PLUGINS}" ]; then \
pnpm run plugins i ${ETHERPAD_PLUGINS} ${ETHERPAD_GITHUB_PLUGINS:+--github ${ETHERPAD_GITHUB_PLUGINS}}; \
fi && \
pnpm store prune
# Copy the configuration file.
COPY --chown=etherpad:etherpad ${SETTINGS} "${EP_DIR}"/settings.json
# Fix group permissions
# Note: For some reason increases image size from 257 to 334.
# RUN chmod -R g=u .
USER etherpad
HEALTHCHECK --interval=5s --timeout=3s \
CMD curl --silent http://localhost:9001/health | grep -E "pass|ok|up" > /dev/null || exit 1
EXPOSE 9001
CMD ["pnpm", "run", "prod"]
================================================
FILE: LICENSE
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2013 THE ETHERPAD FOUNDATION
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: README.md
================================================
# Etherpad: A real-time collaborative editor for the web

## About
Etherpad is a real-time collaborative editor [scalable to thousands of
simultaneous real time users](http://scale.etherpad.org/). It provides [full
data
export](https://github.com/ether/etherpad-lite/wiki/Understanding-Etherpad's-Full-Data-Export-capabilities)
capabilities, and runs on _your_ server, under _your_ control.
## Try it out
[Try out a public Etherpad instance](https://github.com/ether/etherpad-lite/wiki/Sites-That-Run-Etherpad#sites-that-run-etherpad)
## Project Status
We're looking for maintainers and have some funding available. Please contact John McLear if you can help.
### Code Quality
[](https://github.com/ether/etherpad-lite/actions/workflows/codeql-analysis.yml)
### Testing
[](https://github.com/ether/etherpad-lite/actions/workflows/backend-tests.yml)
[](https://github.com/ether/etherpad-lite/actions/workflows/load-test.yml)
[](https://github.com/ether/etherpad-lite/actions/workflows/rate-limit.yml)
[](https://github.com/ether/etherpad-lite/actions/workflows/dockerfile.yml)
[](https://github.com/ether/etherpad-lite/actions/workflows/frontend-admin-tests.yml)
[](https://github.com/ether/etherpad-lite/actions/workflows/frontend-tests.yml)
[](https://saucelabs.com/u/etherpad)
[](https://github.com/ether/etherpad-lite/actions/workflows/windows.yml)
### Engagement
[](https://hub.docker.com/r/etherpad/etherpad)
[](https://discord.com/invite/daEjfhw)
[](https://static.etherpad.org/index.html)


## Installation
### Docker-Compose
```yaml
services:
app:
user: "0:0"
image: etherpad/etherpad:latest
tty: true
stdin_open: true
volumes:
- plugins:/opt/etherpad-lite/src/plugin_packages
- etherpad-var:/opt/etherpad-lite/var
depends_on:
- postgres
environment:
NODE_ENV: production
ADMIN_PASSWORD: ${DOCKER_COMPOSE_APP_ADMIN_PASSWORD:-admin}
DB_CHARSET: ${DOCKER_COMPOSE_APP_DB_CHARSET:-utf8mb4}
DB_HOST: postgres
DB_NAME: ${DOCKER_COMPOSE_POSTGRES_DATABASE:-etherpad}
DB_PASS: ${DOCKER_COMPOSE_POSTGRES_PASSWORD:-admin}
DB_PORT: ${DOCKER_COMPOSE_POSTGRES_PORT:-5432}
DB_TYPE: "postgres"
DB_USER: ${DOCKER_COMPOSE_POSTGRES_USER:-admin}
# For now, the env var DEFAULT_PAD_TEXT cannot be unset or empty; it seems to be mandatory in the latest version of etherpad
DEFAULT_PAD_TEXT: ${DOCKER_COMPOSE_APP_DEFAULT_PAD_TEXT:- }
DISABLE_IP_LOGGING: ${DOCKER_COMPOSE_APP_DISABLE_IP_LOGGING:-false}
SOFFICE: ${DOCKER_COMPOSE_APP_SOFFICE:-null}
TRUST_PROXY: ${DOCKER_COMPOSE_APP_TRUST_PROXY:-true}
restart: always
ports:
- "${DOCKER_COMPOSE_APP_PORT_PUBLISHED:-9001}:${DOCKER_COMPOSE_APP_PORT_TARGET:-9001}"
postgres:
image: postgres:15-alpine
environment:
POSTGRES_DB: ${DOCKER_COMPOSE_POSTGRES_DATABASE:-etherpad}
POSTGRES_PASSWORD: ${DOCKER_COMPOSE_POSTGRES_PASSWORD:-admin}
POSTGRES_PORT: ${DOCKER_COMPOSE_POSTGRES_PORT:-5432}
POSTGRES_USER: ${DOCKER_COMPOSE_POSTGRES_USER:-admin}
PGDATA: /var/lib/postgresql/data/pgdata
restart: always
# Exposing the port is not needed unless you want to access this database instance from the host.
# Be careful when other postgres docker container are running on the same port
# ports:
# - "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
postgres_data:
plugins:
etherpad-var:
```
### Requirements
[Node.js](https://nodejs.org/).
### Windows, macOS, Linux
1. Download the latest Node.js runtime from [nodejs.org](https://nodejs.org/).
2. Install pnpm: `npm install -g pnpm` (Administrator privileges may be required).
3. Clone the repository: `git clone -b master`
4. Run `pnpm i`
5. Run `pnpm run build:etherpad`
6. Run `pnpm run prod`
7. Visit `http://localhost:9001` in your browser.
### Docker container
Find [here](doc/docker.adoc) information on running Etherpad in a container.
## Plugins
Etherpad is very customizable through plugins.


### Available Plugins
For a list of available plugins, see the [plugins
site](https://static.etherpad.org).
### Plugin Installation
You can install plugins from the admin web interface (e.g.,
http://127.0.0.1:9001/admin/plugins).
Alternatively, you can install plugins from the command line:
```sh
cd /path/to/etherpad-lite
pnpm run plugins i ep_${plugin_name}
```
Also see [the plugin wiki
article](https://github.com/ether/etherpad-lite/wiki/Available-Plugins).
### Suggested Plugins
Run the following command in your Etherpad folder to get all of the features
visible in the above demo gif:
```sh
pnpm run plugins i \
ep_align \
ep_comments_page \
ep_embedded_hyperlinks2 \
ep_font_color \
ep_headings2 \
ep_markdown \
ep_webrtc
```
For user authentication, you are encouraged to run an [OpenID
Connect](https://openid.net/connect/) identity provider (OP) and install the
following plugins:
* [ep_openid_connect](https://github.com/ether/ep_openid_connect#readme) to
authenticate against your OP.
* [ep_guest](https://github.com/ether/ep_guest#readme) to create a
"guest" account that has limited access (e.g., read-only access).
* [ep_user_displayname](https://github.com/ether/ep_user_displayname#readme)
to automatically populate each user's displayed name from your OP.
* [ep_stable_authorid](https://github.com/ether/ep_stable_authorid#readme) so
that each user's chosen color, display name, comment ownership, etc. is
strongly linked to their account.
### Upgrade Etherpad
Run the following command in your Etherpad folder to upgrade
1. Stop any running Etherpad (manual, systemd ...)
2. Get present version
```sh
git -P tag --contains
```
3. List versions available
```sh
git -P tag --list "v*" --merged
```
4. Select the version
```sh
git checkout v2.2.5
git switch -c v2.2.5
```
5. Upgrade Etherpad
```sh
./bin/run.sh
```
6. Stop with [CTRL-C]
7. Restart your Etherpad service
## Next Steps
### Tweak the settings
You can modify the settings in `settings.json`. If you need to handle multiple
settings files, you can pass the path to a settings file to `bin/run.sh`
using the `-s|--settings` option: this allows you to run multiple Etherpad
instances from the same installation. Similarly, `--credentials` can be used to
give a settings override file, `--apikey` to give a different APIKEY.txt file
and `--sessionkey` to give a non-default `SESSIONKEY.txt`. **Each configuration
parameter can also be set via an environment variable**, using the syntax
`"${ENV_VAR}"` or `"${ENV_VAR:default_value}"`. For details, refer to
`settings.json.template`. Once you have access to your `/admin` section,
settings can be modified through the web browser.
If you are planning to use Etherpad in a production environment, you should use
a dedicated database such as `mysql`, since the `dirtyDB` database driver is
only for testing and/or development purposes.
### Secure your installation
If you have enabled authentication in `users` section in `settings.json`, it is
a good security practice to **store hashes instead of plain text passwords** in
that file. This is _especially_ advised if you are running a production
installation.
Please install [ep_hash_auth plugin](https://www.npmjs.com/package/ep_hash_auth)
and configure it. If you prefer, `ep_hash_auth` also gives you the option of
storing the users in a custom directory in the file system, without having to
edit `settings.json` and restart Etherpad each time.
### Customize the style with skin variants
Open http://127.0.0.1:9001/p/test#skinvariantsbuilder in your browser and start
playing!

## Helpful resources
The [wiki](https://github.com/ether/etherpad-lite/wiki) is your one-stop
resource for Tutorials and How-to's.
Documentation can be found in `doc/`.
## Development
### Things you should know
You can debug Etherpad using `bin/debugRun.sh`.
You can run Etherpad quickly launching `bin/fastRun.sh`. It's convenient for
developers and advanced users. Be aware that it will skip the dependencies
update, so remember to run `bin/installDeps.sh` after installing a new
dependency or upgrading version.
If you want to find out how Etherpad's `Easysync` works (the library that makes
it really realtime), start with this
[PDF](https://github.com/ether/etherpad-lite/raw/master/doc/easysync/easysync-full-description.pdf)
(complex, but worth reading).
### Contributing
Read our [**Developer
Guidelines**](https://github.com/ether/etherpad-lite/blob/master/CONTRIBUTING.md)
### HTTP API
Etherpad is designed to be easily embeddable and provides a [HTTP
API](https://github.com/ether/etherpad-lite/wiki/HTTP-API) that allows your web
application to manage pads, users and groups. It is recommended to use the
[available client
implementations](https://github.com/ether/etherpad-lite/wiki/HTTP-API-client-libraries)
in order to interact with this API.
OpenAPI (previously swagger) definitions for the API are exposed under
`/api/openapi.json`.
### jQuery plugin
There is a [jQuery plugin](https://github.com/ether/etherpad-lite-jquery-plugin)
that helps you to embed Pads into your website.
### Plugin Framework
Etherpad offers a plugin framework, allowing you to easily add your own
features. By default your Etherpad is extremely light-weight and it's up to you
to customize your experience. Once you have Etherpad installed you should [visit
the plugin page](https://static.etherpad.org/) and take control.
### Translations / Localizations (i18n / l10n)
Etherpad comes with translations into all languages thanks to the team at
[TranslateWiki](https://translatewiki.net/).
If you require translations in [plugins](https://static.etherpad.org/) please
send pull request to each plugin individually.
## FAQ
Visit the **[FAQ](https://github.com/ether/etherpad-lite/wiki/FAQ)**.
## Get in touch
The official channel for contacting the development team is via the [GitHub
issues](https://github.com/ether/etherpad-lite/issues).
For **responsible disclosure of vulnerabilities**, please write a mail to the
maintainers (a.mux@inwind.it and contact@etherpad.org).
Join the official [Etherpad Discord
Channel](https://discord.com/invite/daEjfhw).
## License
[Apache License v2](http://www.apache.org/licenses/LICENSE-2.0.html)
================================================
FILE: SECURITY.md
================================================
# Security Policy
## Reporting a Vulnerability
Please email contact@etherpad.org to report security related issues.
================================================
FILE: admin/.eslintrc.cjs
================================================
module.exports = {
root: true,
env: { browser: true, es2020: true },
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:react-hooks/recommended',
],
ignorePatterns: ['dist', '.eslintrc.cjs'],
parser: '@typescript-eslint/parser',
plugins: ['react-refresh'],
rules: {
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
},
}
================================================
FILE: admin/.gitignore
================================================
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
================================================
FILE: admin/README.md
================================================
# React + TypeScript + Vite
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
Currently, two official plugins are available:
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
## Expanding the ESLint configuration
If you are developing a production application, we recommend updating the configuration to enable type aware lint rules:
- Configure the top-level `parserOptions` property like this:
```js
export default {
// other rules...
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
project: ['./tsconfig.json', './tsconfig.node.json'],
tsconfigRootDir: __dirname,
},
}
```
- Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked`
- Optionally add `plugin:@typescript-eslint/stylistic-type-checked`
- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list
================================================
FILE: admin/index.html
================================================
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Etherpad Admin Dashboard</title>
<link rel="icon" href="/favicon.ico">
</head>
<body>
<div id="root"></div>
<div id="loading"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
================================================
FILE: admin/package.json
================================================
{
"name": "admin",
"private": true,
"version": "2.6.1",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"build-copy": "tsc && vite build --outDir ../src/templates/admin --emptyOutDir",
"preview": "vite preview"
},
"dependencies": {
"@radix-ui/react-switch": "^1.2.6"
},
"devDependencies": {
"@radix-ui/react-dialog": "^1.1.15",
"@radix-ui/react-toast": "^1.2.15",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"@typescript-eslint/eslint-plugin": "^8.57.1",
"@typescript-eslint/parser": "^8.57.1",
"@vitejs/plugin-react": "^6.0.1",
"babel-plugin-react-compiler": "19.1.0-rc.3",
"eslint": "^10.0.3",
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.5.2",
"i18next": "^25.8.19",
"i18next-browser-languagedetector": "^8.2.1",
"lucide-react": "^0.577.0",
"react": "^19.2.4",
"react-dom": "^19.2.4",
"react-hook-form": "^7.71.2",
"react-i18next": "^16.5.8",
"react-router-dom": "^7.13.1",
"socket.io-client": "^4.8.3",
"typescript": "^5.9.3",
"vite": "npm:rolldown-vite@7.2.10",
"vite-plugin-babel": "^1.6.0",
"vite-plugin-static-copy": "^3.3.0",
"zustand": "^5.0.12"
},
"overrides": {
"vite": "npm:rolldown-vite@7.2.10"
}
}
================================================
FILE: admin/public/ep_admin_pads/ar.json
================================================
{
"@metadata": {
"authors": [
"Meno25",
"محمد أحمد عبد الفتاح"
]
},
"ep_adminpads2_action": "فعل",
"ep_adminpads2_autoupdate-label": "التحديث التلقائي على تغييرات الوسادة",
"ep_adminpads2_autoupdate.title": "لتمكين أو تعطيل التحديثات التلقائية للاستعلام الحالي.",
"ep_adminpads2_confirm": "هل تريد حقًا حذف الوسادة {{padID}}؟",
"ep_adminpads2_delete.value": "حذف",
"ep_adminpads2_last-edited": "آخر تعديل",
"ep_adminpads2_loading": "جارٍ التحميل...",
"ep_adminpads2_manage-pads": "إدارة الفوط",
"ep_adminpads2_no-results": "لا توجد نتائج.",
"ep_adminpads2_pad-user-count": "عدد المستخدمين الوسادة",
"ep_adminpads2_padname": "بادنام",
"ep_adminpads2_search-box.placeholder": "مصطلح البحث",
"ep_adminpads2_search-button.value": "بحث",
"ep_adminpads2_search-done": "اكتمل البحث",
"ep_adminpads2_search-error-explanation": "واجه الخادم خطأً أثناء البحث عن منصات:",
"ep_adminpads2_search-error-title": "فشل في الحصول على قائمة الوسادة",
"ep_adminpads2_search-heading": "ابحث عن الفوط",
"ep_adminpads2_title": "إدارة الوسادة",
"ep_adminpads2_unknown-error": "خطأ غير معروف",
"ep_adminpads2_unknown-status": "حالة غير معروفة"
}
================================================
FILE: admin/public/ep_admin_pads/bn.json
================================================
{
"@metadata": {
"authors": [
"আজিজ",
"আফতাবুজ্জামান"
]
},
"ep_adminpads2_action": "কার্য",
"ep_adminpads2_delete.value": "মুছে ফেলুন",
"ep_adminpads2_last-edited": "সর্বশেষ সম্পাদিত",
"ep_adminpads2_loading": "লোড হচ্ছে...",
"ep_adminpads2_manage-pads": "প্যাড পরিচালনা করুন",
"ep_adminpads2_no-results": "ফলাফল নেই",
"ep_adminpads2_padname": "প্যাডের নাম",
"ep_adminpads2_search-button.value": "অনুসন্ধান",
"ep_adminpads2_search-done": "অনুসন্ধান সম্পূর্ণ",
"ep_adminpads2_search-error-explanation": "প্যাড অনুসন্ধান করার সময় সার্ভার একটি ত্রুটির সম্মুখীন হয়েছে:",
"ep_adminpads2_search-error-title": "প্যাডের তালিকা পেতে ব্যর্থ",
"ep_adminpads2_search-heading": "প্যাড অনুসন্ধান করুন",
"ep_adminpads2_title": "প্যাড প্রশাসন",
"ep_adminpads2_unknown-error": "অজানা ত্রুটি",
"ep_adminpads2_unknown-status": "অজানা অবস্থা"
}
================================================
FILE: admin/public/ep_admin_pads/ca.json
================================================
{
"@metadata": {
"authors": [
"Mguix"
]
},
"ep_adminpads2_action": "Acció",
"ep_adminpads2_autoupdate-label": "Actualització automàtica en cas de canvis de pad",
"ep_adminpads2_autoupdate.title": "Activa o desactiva les actualitzacions automàtiques per a la consulta actual.",
"ep_adminpads2_confirm": "Esteu segur que voleu suprimir el pad {{padID}}?",
"ep_adminpads2_delete.value": "Esborrar",
"ep_adminpads2_last-edited": "Darrera modificació",
"ep_adminpads2_loading": "S’està carregant…",
"ep_adminpads2_manage-pads": "Gestiona els pads",
"ep_adminpads2_no-results": "No hi ha cap resultat",
"ep_adminpads2_pad-user-count": "Nombre d'usuaris de pads",
"ep_adminpads2_padname": "Nom del pad",
"ep_adminpads2_search-box.placeholder": "Terme de cerca",
"ep_adminpads2_search-button.value": "Cercar",
"ep_adminpads2_search-done": "Cerca completa",
"ep_adminpads2_search-error-explanation": "El servidor ha trobat un error mentre buscava pads:",
"ep_adminpads2_search-error-title": "No s'ha pogut obtenir la llista del pad",
"ep_adminpads2_search-heading": "Cerca pads",
"ep_adminpads2_title": "Administració del pad",
"ep_adminpads2_unknown-error": "Error desconegut",
"ep_adminpads2_unknown-status": "Estat desconegut"
}
================================================
FILE: admin/public/ep_admin_pads/cs.json
================================================
{
"@metadata": {
"authors": [
"Spotter"
]
},
"ep_adminpads2_action": "Akce",
"ep_adminpads2_autoupdate-label": "Automatická aktualizace změn Padu",
"ep_adminpads2_autoupdate.title": "Povolí nebo zakáže automatické aktualizace pro aktuální dotaz.",
"ep_adminpads2_confirm": "Opravdu chcete odstranit pad {{padID}}?",
"ep_adminpads2_delete.value": "Smazat",
"ep_adminpads2_last-edited": "Naposledy upraveno",
"ep_adminpads2_loading": "Načítání…",
"ep_adminpads2_manage-pads": "Spravovat pady",
"ep_adminpads2_no-results": "Žádné výsledky",
"ep_adminpads2_pad-user-count": "Počet uživatelů padu",
"ep_adminpads2_padname": "Název padu",
"ep_adminpads2_search-box.placeholder": "Hledaný výraz",
"ep_adminpads2_search-button.value": "Hledat",
"ep_adminpads2_search-done": "Hledání dokončeno",
"ep_adminpads2_search-error-explanation": "Při hledání padů došlo k chybě serveru:",
"ep_adminpads2_search-error-title": "Seznam padů se nepodařilo získat",
"ep_adminpads2_search-heading": "Hledat pady",
"ep_adminpads2_title": "Správa Padu",
"ep_adminpads2_unknown-error": "Neznámá chyba",
"ep_adminpads2_unknown-status": "Neznámý stav"
}
================================================
FILE: admin/public/ep_admin_pads/cy.json
================================================
{
"@metadata": {
"authors": [
"Robin Owain"
]
},
"ep_adminpads2_action": "Gweithred",
"ep_adminpads2_autoupdate-label": "Diweddaru newidiadau pad yn otomatig",
"ep_adminpads2_autoupdate.title": "Galluogi neu analluogi diweddaru'r ymholiad cyfredol.",
"ep_adminpads2_confirm": "Siwr eich bod am ddileu'r pad {{padID}}?",
"ep_adminpads2_delete.value": "Dileu",
"ep_adminpads2_last-edited": "Golygwyd ddiwethaf",
"ep_adminpads2_loading": "Wrthi'n llwytho...",
"ep_adminpads2_manage-pads": "Rheoli'r padiau",
"ep_adminpads2_no-results": "Dim canlyniad",
"ep_adminpads2_pad-user-count": "Cyfri defnyddiwr pad",
"ep_adminpads2_padname": "Enwpad",
"ep_adminpads2_search-box.placeholder": "Term chwilio",
"ep_adminpads2_search-button.value": "Chwilio",
"ep_adminpads2_search-done": "Wedi gorffen",
"ep_adminpads2_search-error-explanation": "Nam ar y gweinydd wrth chwilio'r padiau:",
"ep_adminpads2_search-error-title": "Methwyd a chael y rhestr pad",
"ep_adminpads2_search-heading": "Chwilio am badiau",
"ep_adminpads2_title": "Gweinyddiaeth y pad",
"ep_adminpads2_unknown-error": "Nam o ryw fath",
"ep_adminpads2_unknown-status": "Statws anhysbys"
}
================================================
FILE: admin/public/ep_admin_pads/da.json
================================================
{
"@metadata": {
"authors": [
"Saederup92"
]
},
"ep_adminpads2_action": "Handling",
"ep_adminpads2_delete.value": "Slet",
"ep_adminpads2_last-edited": "Sidst redigeret",
"ep_adminpads2_loading": "Indlæser...",
"ep_adminpads2_no-results": "Ingen resultater",
"ep_adminpads2_unknown-error": "Ukendt fejl",
"ep_adminpads2_unknown-status": "Ukendt status"
}
================================================
FILE: admin/public/ep_admin_pads/de.json
================================================
{
"@metadata": {
"authors": [
"Brettchenweber",
"Justman10000",
"Lorisobi",
"SamTV",
"Umlaut",
"Zunkelty"
]
},
"ep_adminpads2_action": "Aktion",
"ep_adminpads2_autoupdate-label": "Automatisch bei Pad-Änderungen updaten",
"ep_adminpads2_autoupdate.title": "Aktiviert oder deaktiviert automatische Aktualisierungen für die aktuelle Abfrage.",
"ep_adminpads2_confirm": "Willst du das Pad {{padID}} wirklich löschen?",
"ep_adminpads2_delete.value": "Löschen",
"ep_adminpads2_cleanup": "Historie aufräumen",
"ep_adminpads2_last-edited": "Zuletzt bearbeitet",
"ep_adminpads2_loading": "Lädt...",
"ep_adminpads2_manage-pads": "Pads verwalten",
"ep_adminpads2_no-results": "Keine Ergebnisse",
"ep_adminpads2_pad-user-count": "Nutzerzahl des Pads",
"ep_adminpads2_padname": "Padname",
"ep_adminpads2_search-box.placeholder": "Suchbegriff",
"ep_adminpads2_search-button.value": "Suche",
"ep_adminpads2_search-done": "Suche vollendet",
"ep_adminpads2_search-error-explanation": "Der Server ist bei der Suche nach Pads auf einen Fehler gestoßen:",
"ep_adminpads2_search-error-title": "Pad-Liste konnte nicht abgerufen werden",
"ep_adminpads2_search-heading": "Nach Pads suchen",
"ep_adminpads2_title": "Pad-Verwaltung",
"ep_adminpads2_unknown-error": "Unbekannter Fehler",
"ep_adminpads2_unknown-status": "Unbekannter Status"
}
================================================
FILE: admin/public/ep_admin_pads/diq.json
================================================
{
"@metadata": {
"authors": [
"1917 Ekim Devrimi",
"Mirzali"
]
},
"ep_adminpads2_action": "Hereketi",
"ep_adminpads2_autoupdate-label": "Vurnayışanê pedi otomatik rocane kerê",
"ep_adminpads2_autoupdate.title": "Persê mewcudi rê rocaneyışanê otomatika aktiv ke ya zi dewrê ra vecê",
"ep_adminpads2_confirm": "Şıma qayılê pedê {{padID}} bıesternê?",
"ep_adminpads2_delete.value": "Bestere",
"ep_adminpads2_last-edited": "Vurnayışo peyên",
"ep_adminpads2_loading": "Bar beno...",
"ep_adminpads2_manage-pads": "Pedan idare kerê",
"ep_adminpads2_no-results": "Netice çıniyo",
"ep_adminpads2_pad-user-count": "Amarê karberanê pedi",
"ep_adminpads2_padname": "Padname",
"ep_adminpads2_search-box.placeholder": "termê cıgêrayış",
"ep_adminpads2_search-button.value": "Cı geyre",
"ep_adminpads2_search-done": "Cıgeyrayışi temam",
"ep_adminpads2_search-error-explanation": "Server cıgeyrayışê pedan de yew xetaya raşt ame",
"ep_adminpads2_search-error-title": "Lista pedi nêgêriye",
"ep_adminpads2_search-heading": "Pedan cıgeyrayış",
"ep_adminpads2_title": "İdarey pedi",
"ep_adminpads2_unknown-error": "Xetaya nêzanıtiye",
"ep_adminpads2_unknown-status": "Weziyeto nêzanaye"
}
================================================
FILE: admin/public/ep_admin_pads/dsb.json
================================================
{
"@metadata": {
"authors": [
"Michawiki"
]
},
"ep_adminpads2_action": "Akcija",
"ep_adminpads2_autoupdate-label": "Pśi změnach na zapisniku awtomatiski aktualizěrowaś",
"ep_adminpads2_autoupdate.title": "Zmóžnja abo znjemóžnja awtomatiske aktualizacije za aktualne wótpšašowanje.",
"ep_adminpads2_confirm": "Cośo napšawdu zapisnik {{padID}} lašowaś?",
"ep_adminpads2_delete.value": "Lašowaś",
"ep_adminpads2_last-edited": "Slědna změna",
"ep_adminpads2_loading": "Zacytujo se...",
"ep_adminpads2_manage-pads": "Zapisniki zastojaś",
"ep_adminpads2_no-results": "Žedne wuslědki",
"ep_adminpads2_pad-user-count": "Licba wužywarjow zapisnika",
"ep_adminpads2_padname": "Mě zapisnika",
"ep_adminpads2_search-box.placeholder": "Pytańske zapśimjeśe",
"ep_adminpads2_search-button.value": "Pytaś",
"ep_adminpads2_search-done": "Pytanje dokóńcone",
"ep_adminpads2_search-error-explanation": "Serwer jo starcył na zmólku, mjaztym až jo pytał za zapisnikami:",
"ep_adminpads2_search-error-title": "Lisćina zapisnikow njedajo se wobstaraś",
"ep_adminpads2_search-heading": "Za zapisnikami pytaś",
"ep_adminpads2_title": "Zapisnikowa administracija",
"ep_adminpads2_unknown-error": "Njeznata zmólka",
"ep_adminpads2_unknown-status": "Njeznaty status"
}
================================================
FILE: admin/public/ep_admin_pads/el.json
================================================
{
"@metadata": {
"authors": [
"Norhorn"
]
},
"ep_adminpads2_delete.value": "Διαγραφή",
"ep_adminpads2_last-edited": "Τελευταία απεξεργασία",
"ep_adminpads2_loading": "Φόρτωση…",
"ep_adminpads2_no-results": "Κανένα αποτέλεσμα",
"ep_adminpads2_search-box.placeholder": "Αναζήτηση όρων",
"ep_adminpads2_search-button.value": "Αναζήτηση",
"ep_adminpads2_search-done": "Ολοκλήρωση αναζήτησης",
"ep_adminpads2_unknown-error": "Άγνωστο σφάλμα",
"ep_adminpads2_unknown-status": "Άγνωστη κατάσταση"
}
================================================
FILE: admin/public/ep_admin_pads/en.json
================================================
{
"ep_adminpads2_action": "Action",
"ep_adminpads2_autoupdate-label": "Auto-update on pad changes",
"ep_adminpads2_autoupdate.title": "Enables or disables automatic updates for the current query.",
"ep_adminpads2_confirm": "Do you really want to delete the pad {{padID}}?",
"ep_adminpads2_delete.value": "Delete",
"ep_adminpads2_cleanup": "Cleanup revisions",
"ep_adminpads2_last-edited": "Last edited",
"ep_adminpads2_loading": "Loading…",
"ep_adminpads2_manage-pads": "Manage pads",
"ep_adminpads2_no-results": "No results",
"ep_adminpads2_pad-user-count": "Pad user count",
"ep_adminpads2_padname": "Padname",
"ep_adminpads2_search-box.placeholder": "Search term",
"ep_adminpads2_search-button.value": "Search",
"ep_adminpads2_search-done": "Search complete",
"ep_adminpads2_search-error-explanation": "The server encountered an error while searching for pads:",
"ep_adminpads2_search-error-title": "Failed to get pad list",
"ep_adminpads2_search-heading": "Search for pads",
"ep_adminpads2_title": "Pad administration",
"ep_adminpads2_unknown-error": "Unknown error",
"ep_adminpads2_unknown-status": "Unknown status"
}
================================================
FILE: admin/public/ep_admin_pads/eu.json
================================================
{
"@metadata": {
"authors": [
"Izendegi"
]
},
"ep_adminpads2_action": "Ekintza",
"ep_adminpads2_autoupdate-label": "Automatikoki eguneratu pad-aren aldaketak daudenean",
"ep_adminpads2_autoupdate.title": "Oraingo kontsultarako eguneratze automatikoak gaitu edo desgaitzen du.",
gitextract_fnv49fja/
├── .dockerignore
├── .editorconfig
├── .gitattributes
├── .github/
│ ├── FUNDING.yml
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.md
│ │ ├── feature_request.md
│ │ ├── plugin-request-template.md
│ │ ├── security-issue.md
│ │ └── security.md
│ ├── PULL_REQUEST_TEMPLATE.md
│ ├── dependabot.yml
│ └── workflows/
│ ├── backend-tests.yml
│ ├── build-and-deploy-docs.yml
│ ├── codeql-analysis.yml
│ ├── dependency-review.yml
│ ├── docker.yml
│ ├── frontend-admin-tests.yml
│ ├── frontend-tests.yml
│ ├── handleRelease.yml
│ ├── load-test.yml
│ ├── perform-type-check.yml
│ ├── rate-limit.yml
│ ├── release.yml
│ ├── releaseEtherpad.yml
│ ├── stale.yml
│ └── upgrade-from-latest-release.yml
├── .gitignore
├── .lgtm.yml
├── .pr_agent.toml
├── .travis.yml
├── AGENTS.MD
├── CHANGELOG.md
├── CONTRIBUTING.md
├── Dockerfile
├── LICENSE
├── README.md
├── SECURITY.md
├── admin/
│ ├── .eslintrc.cjs
│ ├── .gitignore
│ ├── README.md
│ ├── index.html
│ ├── package.json
│ ├── public/
│ │ └── ep_admin_pads/
│ │ ├── ar.json
│ │ ├── bn.json
│ │ ├── ca.json
│ │ ├── cs.json
│ │ ├── cy.json
│ │ ├── da.json
│ │ ├── de.json
│ │ ├── diq.json
│ │ ├── dsb.json
│ │ ├── el.json
│ │ ├── en.json
│ │ ├── eu.json
│ │ ├── ff.json
│ │ ├── fi.json
│ │ ├── fr.json
│ │ ├── gl.json
│ │ ├── he.json
│ │ ├── hsb.json
│ │ ├── hu.json
│ │ ├── ia.json
│ │ ├── it.json
│ │ ├── kn.json
│ │ ├── ko.json
│ │ ├── krc.json
│ │ ├── lb.json
│ │ ├── lt.json
│ │ ├── mk.json
│ │ ├── my.json
│ │ ├── nb.json
│ │ ├── nl.json
│ │ ├── oc.json
│ │ ├── pms.json
│ │ ├── pt-br.json
│ │ ├── pt.json
│ │ ├── qqq.json
│ │ ├── ru.json
│ │ ├── sc.json
│ │ ├── sdc.json
│ │ ├── sk.json
│ │ ├── skr-arab.json
│ │ ├── sl.json
│ │ ├── smn.json
│ │ ├── sms.json
│ │ ├── sq.json
│ │ ├── sv.json
│ │ ├── sw.json
│ │ ├── th.json
│ │ ├── tl.json
│ │ ├── tr.json
│ │ ├── uk.json
│ │ ├── zh-hans.json
│ │ └── zh-hant.json
│ ├── src/
│ │ ├── App.css
│ │ ├── App.tsx
│ │ ├── components/
│ │ │ ├── IconButton.tsx
│ │ │ ├── SearchField.tsx
│ │ │ └── ShoutType.ts
│ │ ├── index.css
│ │ ├── localization/
│ │ │ └── i18n.ts
│ │ ├── main.tsx
│ │ ├── pages/
│ │ │ ├── HelpPage.tsx
│ │ │ ├── HomePage.tsx
│ │ │ ├── LoginScreen.tsx
│ │ │ ├── PadPage.tsx
│ │ │ ├── Plugin.ts
│ │ │ ├── SettingsPage.tsx
│ │ │ └── ShoutPage.tsx
│ │ ├── store/
│ │ │ └── store.ts
│ │ ├── utils/
│ │ │ ├── AnimationFrameHook.ts
│ │ │ ├── LoadingScreen.tsx
│ │ │ ├── PadSearch.ts
│ │ │ ├── Toast.tsx
│ │ │ ├── sorting.ts
│ │ │ ├── useDebounce.ts
│ │ │ └── utils.ts
│ │ └── vite-env.d.ts
│ ├── tsconfig.json
│ ├── tsconfig.node.json
│ └── vite.config.ts
├── best_practices.md
├── bin/
│ ├── buildDebian.sh
│ ├── buildForWindows.sh
│ ├── checkAllPads.ts
│ ├── checkPad.ts
│ ├── cleanRun.sh
│ ├── commonPlugins.ts
│ ├── convertSettings.json.template
│ ├── createRelease.sh
│ ├── createUserSession.ts
│ ├── deb-src/
│ │ ├── DEBIAN/
│ │ │ ├── control
│ │ │ ├── postinst
│ │ │ ├── preinst
│ │ │ └── prerm
│ │ └── sysroot/
│ │ └── etc/
│ │ └── init/
│ │ └── etherpad.conf
│ ├── debugRun.sh
│ ├── deleteAllGroupSessions.ts
│ ├── deletePad.ts
│ ├── extractPadData.ts
│ ├── fastRun.sh
│ ├── functions.sh
│ ├── generateReleaseNotes.ts
│ ├── importSqlFile.ts
│ ├── installDeps.sh
│ ├── installLocalPlugins.sh
│ ├── installOnWindows.bat
│ ├── make_docs.ts
│ ├── migrateDB.ts
│ ├── migrateDirtyDBtoRealDB.ts
│ ├── nsis/
│ │ ├── README.md
│ │ └── etherpad.nsi
│ ├── package.json
│ ├── plugins/
│ │ ├── README.md
│ │ ├── checkPlugin.ts
│ │ ├── getCorePlugins.sh
│ │ ├── lib/
│ │ │ ├── CONTRIBUTING.md
│ │ │ ├── LICENSE
│ │ │ ├── README.md
│ │ │ ├── backend-tests.yml
│ │ │ ├── dependabot.yml
│ │ │ ├── eslintrc.cjs
│ │ │ ├── frontend-tests.yml
│ │ │ ├── gitignore
│ │ │ ├── npmpublish.yml
│ │ │ └── test-and-release.yml
│ │ ├── listOfficialPlugins
│ │ ├── reTestAllPlugins.sh
│ │ ├── stalePlugins.ts
│ │ ├── updateAllPluginsScript.sh
│ │ └── updateCorePlugins.sh
│ ├── plugins.ts
│ ├── push-after-release.sh
│ ├── rebuildPad.ts
│ ├── release.ts
│ ├── repairPad.ts
│ ├── run.sh
│ ├── safeRun.sh
│ ├── tsconfig.json
│ └── updatePlugins.sh
├── doc/
│ ├── .gitignore
│ ├── .vitepress/
│ │ ├── config.mts
│ │ └── theme/
│ │ ├── components/
│ │ │ └── SvgImage.vue
│ │ ├── index.ts
│ │ └── styles/
│ │ └── vars.css
│ ├── api/
│ │ ├── api.adoc
│ │ ├── changeset_library.adoc
│ │ ├── changeset_library.md
│ │ ├── editbar.adoc
│ │ ├── editbar.md
│ │ ├── editorInfo.adoc
│ │ ├── editorInfo.md
│ │ ├── embed_parameters.adoc
│ │ ├── embed_parameters.md
│ │ ├── hooks_client-side.adoc
│ │ ├── hooks_client-side.md
│ │ ├── hooks_overview.adoc
│ │ ├── hooks_overview.md
│ │ ├── hooks_server-side.adoc
│ │ ├── hooks_server-side.md
│ │ ├── http_api.adoc
│ │ ├── http_api.md
│ │ ├── index.md
│ │ ├── pluginfw.adoc
│ │ ├── pluginfw.md
│ │ ├── toolbar.adoc
│ │ └── toolbar.md
│ ├── assets/
│ │ └── style.css
│ ├── cli.md
│ ├── cookies.adoc
│ ├── cookies.md
│ ├── database.adoc
│ ├── demo.md
│ ├── docker.adoc
│ ├── docker.md
│ ├── documentation.adoc
│ ├── documentation.md
│ ├── index.adoc
│ ├── index.md
│ ├── localization.adoc
│ ├── localization.md
│ ├── package.json
│ ├── plugins.adoc
│ ├── plugins.md
│ ├── public/
│ │ └── easysync/
│ │ ├── README.md
│ │ ├── easysync-full-description.tex
│ │ ├── easysync-notes.tex
│ │ └── easysync-notes.txt
│ ├── skins.adoc
│ ├── skins.md
│ ├── stats.adoc
│ └── stats.md
├── docker-compose.dev.yml
├── docker-compose.yml
├── local_plugins/
│ └── .gitignore
├── package.json
├── pnpm-workspace.yaml
├── settings.json.docker
├── settings.json.template
├── src/
│ ├── .eslintrc.cjs
│ ├── README.md
│ ├── ep.json
│ ├── locales/
│ │ ├── af.json
│ │ ├── ar.json
│ │ ├── ast.json
│ │ ├── awa.json
│ │ ├── az.json
│ │ ├── azb.json
│ │ ├── bcc.json
│ │ ├── be-tarask.json
│ │ ├── bg.json
│ │ ├── bgn.json
│ │ ├── bn.json
│ │ ├── br.json
│ │ ├── bs.json
│ │ ├── ca.json
│ │ ├── ce.json
│ │ ├── cs.json
│ │ ├── da.json
│ │ ├── de.json
│ │ ├── diq.json
│ │ ├── dsb.json
│ │ ├── dty.json
│ │ ├── el.json
│ │ ├── en-gb.json
│ │ ├── en.json
│ │ ├── eo.json
│ │ ├── es.json
│ │ ├── et.json
│ │ ├── eu.json
│ │ ├── fa.json
│ │ ├── ff.json
│ │ ├── fi.json
│ │ ├── fo.json
│ │ ├── fr.json
│ │ ├── fy.json
│ │ ├── ga.json
│ │ ├── gl.json
│ │ ├── got.json
│ │ ├── gu.json
│ │ ├── he.json
│ │ ├── hi.json
│ │ ├── hr.json
│ │ ├── hrx.json
│ │ ├── hsb.json
│ │ ├── hu.json
│ │ ├── hy.json
│ │ ├── ia.json
│ │ ├── id.json
│ │ ├── is.json
│ │ ├── it.json
│ │ ├── ja.json
│ │ ├── kab.json
│ │ ├── km.json
│ │ ├── kn.json
│ │ ├── ko.json
│ │ ├── krc.json
│ │ ├── ksh.json
│ │ ├── ku-latn.json
│ │ ├── lb.json
│ │ ├── lki.json
│ │ ├── lrc.json
│ │ ├── lt.json
│ │ ├── lv.json
│ │ ├── map-bms.json
│ │ ├── mg.json
│ │ ├── mk.json
│ │ ├── ml.json
│ │ ├── mn.json
│ │ ├── mnw.json
│ │ ├── mr.json
│ │ ├── ms.json
│ │ ├── my.json
│ │ ├── nah.json
│ │ ├── nap.json
│ │ ├── nb.json
│ │ ├── nds.json
│ │ ├── ne.json
│ │ ├── nl.json
│ │ ├── nn.json
│ │ ├── oc.json
│ │ ├── olo.json
│ │ ├── os.json
│ │ ├── pa.json
│ │ ├── pl.json
│ │ ├── pms.json
│ │ ├── ps.json
│ │ ├── pt-br.json
│ │ ├── pt.json
│ │ ├── qqq.json
│ │ ├── ro.json
│ │ ├── ru.json
│ │ ├── sc.json
│ │ ├── sco.json
│ │ ├── sd.json
│ │ ├── sh-latn.json
│ │ ├── shn.json
│ │ ├── sk.json
│ │ ├── skr-arab.json
│ │ ├── sl.json
│ │ ├── sms.json
│ │ ├── sq.json
│ │ ├── sr-ec.json
│ │ ├── sr-el.json
│ │ ├── sro.json
│ │ ├── sv.json
│ │ ├── sw.json
│ │ ├── ta.json
│ │ ├── tcy.json
│ │ ├── te.json
│ │ ├── th.json
│ │ ├── tr.json
│ │ ├── uk.json
│ │ ├── vec.json
│ │ ├── vi.json
│ │ ├── zh-hans.json
│ │ └── zh-hant.json
│ ├── node/
│ │ ├── README.md
│ │ ├── db/
│ │ │ ├── API.ts
│ │ │ ├── AuthorManager.ts
│ │ │ ├── DB.ts
│ │ │ ├── GroupManager.ts
│ │ │ ├── Pad.ts
│ │ │ ├── PadManager.ts
│ │ │ ├── ReadOnlyManager.ts
│ │ │ ├── SecurityManager.ts
│ │ │ ├── SessionManager.ts
│ │ │ └── SessionStore.ts
│ │ ├── eejs/
│ │ │ └── index.ts
│ │ ├── handler/
│ │ │ ├── APIHandler.ts
│ │ │ ├── APIKeyHandler.ts
│ │ │ ├── ExportHandler.ts
│ │ │ ├── ImportHandler.ts
│ │ │ ├── PadMessageHandler.ts
│ │ │ ├── RestAPI.ts
│ │ │ └── SocketIORouter.ts
│ │ ├── hooks/
│ │ │ ├── express/
│ │ │ │ ├── admin.ts
│ │ │ │ ├── adminplugins.ts
│ │ │ │ ├── adminsettings.ts
│ │ │ │ ├── apicalls.ts
│ │ │ │ ├── errorhandling.ts
│ │ │ │ ├── importexport.ts
│ │ │ │ ├── openapi.ts
│ │ │ │ ├── padurlsanitize.ts
│ │ │ │ ├── pwa.ts
│ │ │ │ ├── socketio.ts
│ │ │ │ ├── specialpages.ts
│ │ │ │ ├── static.ts
│ │ │ │ ├── tokenTransfer.ts
│ │ │ │ └── webaccess.ts
│ │ │ ├── express.ts
│ │ │ └── i18n.ts
│ │ ├── metrics.ts
│ │ ├── padaccess.ts
│ │ ├── prometheus.ts
│ │ ├── security/
│ │ │ ├── OAuth2Provider.ts
│ │ │ ├── OAuth2User.ts
│ │ │ ├── OIDCAdapter.ts
│ │ │ ├── SecretRotator.ts
│ │ │ └── crypto.ts
│ │ ├── server.ts
│ │ ├── stats.ts
│ │ ├── types/
│ │ │ ├── APIHandlerType.ts
│ │ │ ├── ArgsExpressType.ts
│ │ │ ├── AsyncQueueTask.ts
│ │ │ ├── ChangeSet.ts
│ │ │ ├── DeriveModel.ts
│ │ │ ├── ErrorCaused.ts
│ │ │ ├── I18nPluginDefs.ts
│ │ │ ├── LegacyParams.ts
│ │ │ ├── MapType.ts
│ │ │ ├── PackageInfo.ts
│ │ │ ├── PadSearchQuery.ts
│ │ │ ├── PadType.ts
│ │ │ ├── PartType.ts
│ │ │ ├── Plugin.ts
│ │ │ ├── PromiseWithStd.ts
│ │ │ ├── QueryType.ts
│ │ │ ├── Revision.ts
│ │ │ ├── RunCMDOptions.ts
│ │ │ ├── SecretRotatorType.ts
│ │ │ ├── SettingsUser.ts
│ │ │ ├── SocketAcknowledge.ts
│ │ │ ├── SocketClientRequest.ts
│ │ │ ├── SocketModule.ts
│ │ │ ├── SwaggerUIResource.ts
│ │ │ ├── UserSettingsObject.ts
│ │ │ └── WebAccessTypes.ts
│ │ └── utils/
│ │ ├── Abiword.ts
│ │ ├── AbsolutePaths.ts
│ │ ├── Cleanup.ts
│ │ ├── Cli.ts
│ │ ├── ExportEtherpad.ts
│ │ ├── ExportHelper.ts
│ │ ├── ExportHtml.ts
│ │ ├── ExportTxt.ts
│ │ ├── ImportEtherpad.ts
│ │ ├── ImportHtml.ts
│ │ ├── LibreOffice.ts
│ │ ├── Minify.ts
│ │ ├── MinifyWorker.ts
│ │ ├── NodeVersion.ts
│ │ ├── Settings.ts
│ │ ├── SettingsTree.ts
│ │ ├── Stream.ts
│ │ ├── UpdateCheck.ts
│ │ ├── checkValidRev.ts
│ │ ├── customError.ts
│ │ ├── padDiff.ts
│ │ ├── path_exists.ts
│ │ ├── promises.ts
│ │ ├── randomstring.ts
│ │ ├── run_cmd.ts
│ │ ├── sanitizePathname.ts
│ │ ├── tar.json
│ │ └── toolbar.ts
│ ├── package.json
│ ├── playwright.config.ts
│ ├── static/
│ │ ├── css/
│ │ │ ├── admin.css
│ │ │ ├── iframe_editor.css
│ │ │ ├── lists_and_indents.css
│ │ │ ├── pad/
│ │ │ │ ├── chat.css
│ │ │ │ ├── fonts.css
│ │ │ │ ├── form.css
│ │ │ │ ├── gritter.css
│ │ │ │ ├── icons.css
│ │ │ │ ├── layout.css
│ │ │ │ ├── loadingbox.css
│ │ │ │ ├── normalize.css
│ │ │ │ ├── popup.css
│ │ │ │ ├── popup_connectivity.css
│ │ │ │ ├── popup_import_export.css
│ │ │ │ ├── popup_users.css
│ │ │ │ └── toolbar.css
│ │ │ ├── pad.css
│ │ │ └── timeslider.css
│ │ ├── empty.html
│ │ ├── font/
│ │ │ ├── Montserrat-Light.otf
│ │ │ ├── Montserrat-Regular.otf
│ │ │ ├── config.json
│ │ │ └── opendyslexic.otf
│ │ ├── js/
│ │ │ ├── AttributeManager.ts
│ │ │ ├── AttributeMap.ts
│ │ │ ├── AttributePool.ts
│ │ │ ├── Builder.ts
│ │ │ ├── Changeset.ts
│ │ │ ├── ChangesetUtils.ts
│ │ │ ├── ChatMessage.ts
│ │ │ ├── MergingOpAssembler.ts
│ │ │ ├── Op.ts
│ │ │ ├── OpAssembler.ts
│ │ │ ├── OpIter.ts
│ │ │ ├── SmartOpAssembler.ts
│ │ │ ├── StringAssembler.ts
│ │ │ ├── StringIterator.ts
│ │ │ ├── TextLinesMutator.ts
│ │ │ ├── ace.ts
│ │ │ ├── ace2_common.ts
│ │ │ ├── ace2_inner.ts
│ │ │ ├── attributes.ts
│ │ │ ├── basic_error_handler.ts
│ │ │ ├── broadcast.ts
│ │ │ ├── broadcast_revisions.ts
│ │ │ ├── broadcast_slider.ts
│ │ │ ├── caretPosition.ts
│ │ │ ├── changesettracker.ts
│ │ │ ├── chat.ts
│ │ │ ├── collab_client.ts
│ │ │ ├── colorutils.ts
│ │ │ ├── contentcollector.ts
│ │ │ ├── cssmanager.ts
│ │ │ ├── domline.ts
│ │ │ ├── index.ts
│ │ │ ├── l10n.ts
│ │ │ ├── linestylefilter.ts
│ │ │ ├── pad.ts
│ │ │ ├── pad_automatic_reconnect.ts
│ │ │ ├── pad_connectionstatus.ts
│ │ │ ├── pad_cookie.ts
│ │ │ ├── pad_editbar.ts
│ │ │ ├── pad_editor.ts
│ │ │ ├── pad_impexp.ts
│ │ │ ├── pad_modals.ts
│ │ │ ├── pad_savedrevs.ts
│ │ │ ├── pad_userlist.ts
│ │ │ ├── pad_utils.ts
│ │ │ ├── pluginfw/
│ │ │ │ ├── LinkInstaller.ts
│ │ │ │ ├── client_plugins.ts
│ │ │ │ ├── hooks.ts
│ │ │ │ ├── installer.ts
│ │ │ │ ├── plugin_defs.ts
│ │ │ │ ├── plugins.ts
│ │ │ │ ├── shared.ts
│ │ │ │ └── tsort.ts
│ │ │ ├── rjquery.ts
│ │ │ ├── scroll.ts
│ │ │ ├── security.ts
│ │ │ ├── skin_variants.ts
│ │ │ ├── skiplist.ts
│ │ │ ├── socketio.ts
│ │ │ ├── timeslider.ts
│ │ │ ├── types/
│ │ │ │ ├── AText.ts
│ │ │ │ ├── Attribute.ts
│ │ │ │ ├── ChangeSet.ts
│ │ │ │ ├── ChangeSetBuilder.ts
│ │ │ │ ├── PadRevision.ts
│ │ │ │ ├── RepModel.ts
│ │ │ │ └── SocketIOMessage.ts
│ │ │ ├── underscore.ts
│ │ │ ├── undomodule.ts
│ │ │ ├── vendors/
│ │ │ │ ├── browser.ts
│ │ │ │ ├── farbtastic.ts
│ │ │ │ ├── gritter.ts
│ │ │ │ ├── html10n.ts
│ │ │ │ ├── jquery.ts
│ │ │ │ └── nice-select.ts
│ │ │ └── welcome.ts
│ │ ├── robots.txt
│ │ ├── skins/
│ │ │ ├── colibris/
│ │ │ │ ├── index.css
│ │ │ │ ├── index.js
│ │ │ │ ├── pad.css
│ │ │ │ ├── pad.js
│ │ │ │ ├── src/
│ │ │ │ │ ├── components/
│ │ │ │ │ │ ├── buttons.css
│ │ │ │ │ │ ├── chat.css
│ │ │ │ │ │ ├── form.css
│ │ │ │ │ │ ├── gritter.css
│ │ │ │ │ │ ├── import-export.css
│ │ │ │ │ │ ├── popup.css
│ │ │ │ │ │ ├── scrollbars.css
│ │ │ │ │ │ ├── sidediv.css
│ │ │ │ │ │ ├── table-of-content.css
│ │ │ │ │ │ ├── toolbar.css
│ │ │ │ │ │ └── users.css
│ │ │ │ │ ├── general.css
│ │ │ │ │ ├── layout.css
│ │ │ │ │ ├── pad-editor.css
│ │ │ │ │ ├── pad-variants.css
│ │ │ │ │ └── plugins/
│ │ │ │ │ ├── author_hover.css
│ │ │ │ │ ├── brightcolorpicker.css
│ │ │ │ │ ├── comments.css
│ │ │ │ │ ├── font_color.css
│ │ │ │ │ ├── set_title_on_pad.css
│ │ │ │ │ └── tables2.css
│ │ │ │ ├── timeslider.css
│ │ │ │ └── timeslider.js
│ │ │ └── no-skin/
│ │ │ ├── index.css
│ │ │ ├── index.js
│ │ │ ├── pad.css
│ │ │ ├── pad.js
│ │ │ ├── timeslider.css
│ │ │ └── timeslider.js
│ │ └── tests.html
│ ├── templates/
│ │ ├── export_html.html
│ │ ├── index.html
│ │ ├── indexBootstrap.js
│ │ ├── javascript.html
│ │ ├── pad.html
│ │ ├── padBootstrap.js
│ │ ├── padViteBootstrap.js
│ │ ├── timeSliderBootstrap.js
│ │ └── timeslider.html
│ ├── tests/
│ │ ├── README.md
│ │ ├── backend/
│ │ │ ├── common.ts
│ │ │ ├── fuzzImportTest.ts
│ │ │ └── specs/
│ │ │ ├── ExportEtherpad.ts
│ │ │ ├── ImportEtherpad.ts
│ │ │ ├── Pad.ts
│ │ │ ├── SecretRotator.ts
│ │ │ ├── SessionStore.ts
│ │ │ ├── Stream.ts
│ │ │ ├── api/
│ │ │ │ ├── api.ts
│ │ │ │ ├── characterEncoding.ts
│ │ │ │ ├── chat.ts
│ │ │ │ ├── emojis.html
│ │ │ │ ├── fuzzImportTest.ts
│ │ │ │ ├── importexport.ts
│ │ │ │ ├── importexportGetPost.ts
│ │ │ │ ├── instance.ts
│ │ │ │ ├── pad.ts
│ │ │ │ ├── restoreRevision.ts
│ │ │ │ ├── sessionsAndGroups.ts
│ │ │ │ ├── test.doc
│ │ │ │ ├── test.docx
│ │ │ │ ├── test.etherpad
│ │ │ │ ├── test.odt
│ │ │ │ └── test.txt
│ │ │ ├── chat.ts
│ │ │ ├── contentcollector.ts
│ │ │ ├── crypto.ts
│ │ │ ├── export.ts
│ │ │ ├── favicon.ts
│ │ │ ├── health.ts
│ │ │ ├── hooks.ts
│ │ │ ├── lowerCasePadIds.ts
│ │ │ ├── messages.ts
│ │ │ ├── pads-with-spaces.ts
│ │ │ ├── regression-db.ts
│ │ │ ├── settings.json
│ │ │ ├── settings.ts
│ │ │ ├── socketio.ts
│ │ │ ├── specialpages.ts
│ │ │ └── webaccess.ts
│ │ ├── backend-new/
│ │ │ ├── easysync-helper.ts
│ │ │ └── specs/
│ │ │ ├── AttributeMap.ts
│ │ │ ├── StringIteratorTest.ts
│ │ │ ├── admin_utils.ts
│ │ │ ├── attributes.ts
│ │ │ ├── easysync-assembler.ts
│ │ │ ├── easysync-compose.ts
│ │ │ ├── easysync-inverseRandom.ts
│ │ │ ├── easysync-mutations.ts
│ │ │ ├── easysync-other.test.ts
│ │ │ ├── easysync-subAttribution.ts
│ │ │ ├── pad_utils.ts
│ │ │ ├── path_exists.ts
│ │ │ ├── promises.ts
│ │ │ ├── sanitizePathname.ts
│ │ │ └── skiplist.ts
│ │ ├── container/
│ │ │ ├── loadSettings.js
│ │ │ └── specs/
│ │ │ └── api/
│ │ │ └── pad.js
│ │ ├── frontend/
│ │ │ ├── cypress/
│ │ │ │ ├── .gitignore
│ │ │ │ ├── README.md
│ │ │ │ ├── cypress.config.js
│ │ │ │ └── integration/
│ │ │ │ └── test.js
│ │ │ ├── easysync-helper.js
│ │ │ ├── helper/
│ │ │ │ ├── methods.ts
│ │ │ │ ├── multipleUsers.ts
│ │ │ │ └── ui.ts
│ │ │ ├── helper.js
│ │ │ ├── index.html
│ │ │ ├── runner.css
│ │ │ ├── runner.js
│ │ │ ├── specs/
│ │ │ │ ├── authorship_of_editions.js
│ │ │ │ ├── chat_hooks.js
│ │ │ │ ├── chat_load_messages.js
│ │ │ │ ├── drag_and_drop.js
│ │ │ │ ├── easysync-follow.js
│ │ │ │ ├── helper.js
│ │ │ │ ├── importexport.js
│ │ │ │ ├── importindents.js
│ │ │ │ ├── multiple_authors_clear_authorship_colors.js
│ │ │ │ ├── pad_modal.js
│ │ │ │ ├── responsiveness.js
│ │ │ │ ├── scrollTo.js
│ │ │ │ ├── select_formatting_buttons.js
│ │ │ │ ├── timeslider_labels.js
│ │ │ │ ├── timeslider_numeric_padID.js
│ │ │ │ ├── timeslider_revisions.js
│ │ │ │ └── xxauto_reconnect.js
│ │ │ └── travis/
│ │ │ ├── .gitignore
│ │ │ ├── adminrunner.sh
│ │ │ ├── remote_runner.js
│ │ │ ├── runner.sh
│ │ │ ├── runnerBackend.sh
│ │ │ └── runnerLoadTest.sh
│ │ ├── frontend-new/
│ │ │ ├── admin-spec/
│ │ │ │ ├── adminsettings.spec.ts
│ │ │ │ ├── admintroubleshooting.spec.ts
│ │ │ │ └── adminupdateplugins.spec.ts
│ │ │ ├── helper/
│ │ │ │ ├── adminhelper.ts
│ │ │ │ ├── padHelper.ts
│ │ │ │ ├── settingsHelper.ts
│ │ │ │ └── timeslider.ts
│ │ │ └── specs/
│ │ │ ├── alphabet.spec.ts
│ │ │ ├── bold.spec.ts
│ │ │ ├── change_user_color.spec.ts
│ │ │ ├── change_user_name.spec.ts
│ │ │ ├── chat.spec.ts
│ │ │ ├── clear_authorship_color.spec.ts
│ │ │ ├── collab_client.spec.ts
│ │ │ ├── delete.spec.ts
│ │ │ ├── editbar.spec.ts
│ │ │ ├── embed_value.spec.ts
│ │ │ ├── enter.spec.ts
│ │ │ ├── font_type.spec.ts
│ │ │ ├── indentation.spec.ts
│ │ │ ├── inner_height.spec.ts
│ │ │ ├── italic.spec.ts
│ │ │ ├── language.spec.ts
│ │ │ ├── ordered_list.spec.ts
│ │ │ ├── redo.spec.ts
│ │ │ ├── strikethrough.spec.ts
│ │ │ ├── timeslider.spec.ts
│ │ │ ├── timeslider_follow.spec.ts
│ │ │ ├── undo.spec.ts
│ │ │ ├── unordered_list.spec.ts
│ │ │ ├── urls_become_clickable.spec.ts
│ │ │ └── welcome.spec.test.ts
│ │ ├── ratelimit/
│ │ │ ├── Dockerfile.anotherip
│ │ │ ├── Dockerfile.nginx
│ │ │ ├── nginx.conf
│ │ │ ├── send_changesets.js
│ │ │ └── testlimits.sh
│ │ └── settings.json
│ ├── tsconfig.json
│ ├── vitest.config.ts
│ └── web.config
├── start.bat
├── ui/
│ ├── .gitignore
│ ├── consent.html
│ ├── login.html
│ ├── package.json
│ ├── pad.html
│ ├── src/
│ │ ├── consent.ts
│ │ ├── main.ts
│ │ ├── style.css
│ │ └── vite-env.d.ts
│ ├── tsconfig.json
│ └── vite.config.ts
└── var/
└── .gitignore
SYMBOL INDEX (702 symbols across 130 files)
FILE: admin/src/App.tsx
constant WS_URL (line 11) | const WS_URL = import.meta.env.DEV ? 'http://localhost:9001' : ''
FILE: admin/src/components/IconButton.tsx
type IconButtonProps (line 3) | type IconButtonProps = {
FILE: admin/src/components/SearchField.tsx
type SearchFieldProps (line 3) | type SearchFieldProps = {
FILE: admin/src/components/ShoutType.ts
type ShoutType (line 1) | type ShoutType = {
FILE: admin/src/pages/LoginScreen.tsx
type Inputs (line 7) | type Inputs = {
FILE: admin/src/pages/PadPage.tsx
type PadCreateProps (line 13) | type PadCreateProps = {
type SettingsSocketCreateReponse (line 80) | type SettingsSocketCreateReponse = {
FILE: admin/src/pages/Plugin.ts
type PluginDef (line 1) | type PluginDef = {
type InstalledPlugin (line 10) | type InstalledPlugin = {
type SearchParams (line 19) | type SearchParams = {
type HelpObj (line 28) | type HelpObj = {
FILE: admin/src/store/store.ts
type ToastState (line 6) | type ToastState = {
type StoreState (line 14) | type StoreState = {
FILE: admin/src/utils/AnimationFrameHook.ts
type Args (line 3) | type Args = any[]
FILE: admin/src/utils/PadSearch.ts
type PadSearchQuery (line 1) | type PadSearchQuery = {
type PadSearchResult (line 10) | type PadSearchResult = {
type PadType (line 15) | type PadType = {
FILE: bin/make_docs.ts
constant VERSION (line 7) | const VERSION=pjson.version
function copyFolderSync (line 17) | function copyFolderSync(from: fs.PathLike, to: fs.PathLike) {
FILE: bin/migrateDB.ts
type SettingsConfig (line 17) | type SettingsConfig = {
FILE: bin/plugins.ts
function run (line 34) | async function run() {
FILE: doc/.vitepress/theme/index.ts
method enhanceApp (line 9) | enhanceApp({ app }) {
FILE: src/node/db/Pad.ts
class Pad (line 41) | class Pad {
method constructor (line 58) | constructor(id:string, database = db) {
method apool (line 69) | apool() {
method getHeadRevisionNumber (line 73) | getHeadRevisionNumber() {
method getSavedRevisionsNumber (line 77) | getSavedRevisionsNumber() {
method getSavedRevisionsList (line 81) | getSavedRevisionsList() {
method getPublicStatus (line 87) | getPublicStatus() {
method appendRevision (line 97) | async appendRevision(aChangeset:string, authorId = '') {
method toJSON (line 146) | toJSON() {
method saveToDatabase (line 156) | async saveToDatabase() {
method getLastEdit (line 162) | async getLastEdit() {
method getRevisionChangeset (line 168) | async getRevisionChangeset(revNum: number) {
method getRevisionAuthor (line 173) | async getRevisionAuthor(revNum: number) {
method getRevisionDate (line 178) | async getRevisionDate(revNum: number) {
method _getKeyRevisionAText (line 187) | async _getKeyRevisionAText(revNum: number) {
method getAllAuthors (line 196) | getAllAuthors() {
method getInternalRevisionAText (line 208) | async getInternalRevisionAText(targetRev: number) {
method getRevision (line 223) | async getRevision(revNum: number) {
method getAllAuthorColors (line 227) | async getAllAuthorColors() {
method getValidRevisionRange (line 241) | getValidRevisionRange(startRev: any, endRev:any) {
method getKeyRevisionNumber (line 262) | getKeyRevisionNumber(revNum: number) {
method text (line 269) | text(): string {
method spliceText (line 284) | async spliceText(start:number, ndel:number, ins: string, authorId: str...
method setText (line 309) | async setText(newText: string, authorId = '') {
method appendText (line 320) | async appendText(newText:string, authorId = '') {
method appendChatMessage (line 334) | async appendChatMessage(msgOrText: string| ChatMessage, authorId = nul...
method getChatMessage (line 351) | async getChatMessage(entryNum: number) {
method getChatMessages (line 366) | async getChatMessages(start: string, end: number) {
method init (line 382) | async init(text:string, authorId = '') {
method copy (line 403) | async copy(destinationID: string, force: boolean) {
method checkIfGroupExistAndReturnIt (line 456) | async checkIfGroupExistAndReturnIt(destinationID: string) {
method removePadIfForceIsTrueAndAlreadyExist (line 471) | async removePadIfForceIsTrueAndAlreadyExist(destinationID: string, for...
method copyAuthorInfoToDestinationPad (line 494) | async copyAuthorInfoToDestinationPad(destinationID: string) {
method copyPadWithoutHistory (line 500) | async copyPadWithoutHistory(destinationID: string, force: string|boole...
method remove (line 557) | async remove() {
method setPublicStatus (line 618) | async setPublicStatus(publicStatus: boolean) {
method addSavedRevision (line 623) | async addSavedRevision(revNum: string, savedById: string, label: strin...
method getSavedRevisions (line 644) | getSavedRevisions() {
method check (line 651) | async check() {
FILE: src/node/db/PadManager.ts
method get (line 42) | get(name: string)
method set (line 46) | set(name: string, value: any)
method remove (line 50) | remove(name: string) {
method constructor (line 64) | constructor() {
method getPads (line 74) | async getPads() {
method addPad (line 87) | addPad(name: string) {
method removePad (line 93) | removePad(name: string) {
FILE: src/node/db/SecurityManager.ts
constant DENY (line 35) | const DENY = Object.freeze({accessStatus: 'deny'});
FILE: src/node/db/SessionStore.ts
class SessionStore (line 12) | class SessionStore extends expressSession.Store {
method constructor (line 24) | constructor(refresh: number | null = null) {
method shutdown (line 35) | shutdown() {
method _updateExpirations (line 39) | async _updateExpirations(sid: string, sess: any, updateDbExp = true) {
method _write (line 69) | async _write(sid: string, sess: any) {
method _get (line 73) | async _get(sid: string) {
method _set (line 79) | async _set(sid: string, sess:any) {
method _destroy (line 85) | async _destroy(sid:string) {
method _touch (line 95) | async _touch(sid: string, sess:any) {
FILE: src/node/handler/APIKeyHandler.ts
type APIFields (line 12) | type APIFields = {
FILE: src/node/handler/ImportHandler.ts
class ImportError (line 39) | class ImportError extends Error {
method constructor (line 41) | constructor(status: string, ...args:any) {
FILE: src/node/handler/PadMessageHandler.ts
function getTotalActiveUsers (line 97) | function getTotalActiveUsers() {
function getActivePadCountFromSessionInfos (line 103) | function getActivePadCountFromSessionInfos() {
class Channels (line 121) | class Channels {
method constructor (line 128) | constructor(exec = (ch: string, task:any) => task(ch)) {
method enqueue (line 141) | async enqueue(ch:any, task:any): Promise<any> {
type ShoutMessage (line 241) | type ShoutMessage = {
method client (line 361) | get client() {
FILE: src/node/handler/RestAPI.ts
type RestAPIMapping (line 14) | type RestAPIMapping = {
constant GET (line 28) | const GET = "GET"
constant POST (line 29) | const POST = "POST"
constant PUT (line 30) | const PUT = "PUT"
constant DELETE (line 31) | const DELETE = "DELETE"
constant PATCH (line 32) | const PATCH = "PATCH"
FILE: src/node/hooks/express/admin.ts
constant ADMIN_PATH (line 9) | const ADMIN_PATH = path.join(settings.root, 'src', 'templates');
constant PROXY_HEADER (line 10) | const PROXY_HEADER = "x-proxy-path"
FILE: src/node/hooks/express/adminsettings.ts
type ShoutMessage (line 53) | type ShoutMessage = {
function mapToObject (line 82) | function mapToObject(map: Map<string, any>) {
type PadCreationOptions (line 254) | type PadCreationOptions = {
FILE: src/node/hooks/express/apicalls.ts
function objectAsString (line 12) | function objectAsString(obj: any): string {
FILE: src/node/hooks/express/openapi.ts
constant OPENAPI_VERSION (line 33) | const OPENAPI_VERSION = '3.0.2';
FILE: src/node/hooks/express/specialpages.ts
function handleUpdate (line 159) | function handleUpdate() {
FILE: src/node/hooks/express/tokenTransfer.ts
type TokenTransferRequest (line 6) | type TokenTransferRequest = {
FILE: src/node/security/OAuth2Provider.ts
method defaultResource (line 99) | defaultResource(ctx) {
method getResourceServerInfo (line 102) | getResourceServerInfo(ctx, resourceIndicator, client) {
method useGrantedResource (line 109) | useGrantedResource(ctx, model) {
FILE: src/node/security/OAuth2User.ts
type OAuth2User (line 1) | type OAuth2User = {
FILE: src/node/security/OIDCAdapter.ts
function grantKeyFor (line 27) | function grantKeyFor(id: string) {
function userCodeKeyFor (line 31) | function userCodeKeyFor(userCode:string) {
class MemoryAdapter (line 35) | class MemoryAdapter implements Adapter{
method constructor (line 37) | constructor(name:string) {
method key (line 41) | key(id:string) {
method destroy (line 45) | destroy(id:string) {
method consume (line 62) | consume(id: string) {
method find (line 67) | find(id: string): Promise<AdapterPayload | void | undefined> {
method findByUserCode (line 74) | findByUserCode(userCode: string) {
method upsert (line 79) | upsert(id: string, payload: {
method findByUid (line 95) | findByUid(uid: string): Promise<AdapterPayload | void | undefined> {
method revokeByGrantId (line 104) | revokeByGrantId(grantId: string): Promise<void | undefined> {
FILE: src/node/security/SecretRotator.ts
class Kdf (line 11) | class Kdf {
method generateParams (line 12) | async generateParams(): Promise<{ salt: string; digest: string; keyLen...
method derive (line 13) | async derive(params: DeriveModel, info: any) { throw new Error('not im...
class LegacyStaticSecret (line 16) | class LegacyStaticSecret extends Kdf {
method derive (line 17) | async derive(params:any, info:any) { return params; }
class Hkdf (line 20) | class Hkdf extends Kdf {
method constructor (line 23) | constructor(digest:string, keyLen:number) {
method generateParams (line 29) | async generateParams(): Promise<{ salt: string; digest: string; keyLen...
method derive (line 37) | async derive(p: DeriveModel, info:any) {
class SecretRotator (line 66) | class SecretRotator {
method constructor (line 84) | constructor(dbPrefix: string, interval: number, lifetime: number, lega...
method _publish (line 110) | async _publish(params: LegacyParams, id:string|null = null) {
method start (line 118) | async start() {
method stop (line 124) | stop() {
method _deriveSecrets (line 130) | async _deriveSecrets(p: any, now: number) {
method _update (line 168) | async _update() {
FILE: src/node/types/ArgsExpressType.ts
type ArgsExpressType (line 5) | type ArgsExpressType = {
FILE: src/node/types/AsyncQueueTask.ts
type AsyncQueueTask (line 1) | type AsyncQueueTask = {
FILE: src/node/types/ChangeSet.ts
type ChangeSet (line 1) | type ChangeSet = {
FILE: src/node/types/DeriveModel.ts
type DeriveModel (line 1) | type DeriveModel = {
FILE: src/node/types/ErrorCaused.ts
class ErrorCaused (line 1) | class ErrorCaused extends Error {
method constructor (line 4) | constructor(message: string, cause: Error) {
type ErrorCause (line 12) | type ErrorCause = {
FILE: src/node/types/I18nPluginDefs.ts
type I18nPluginDefs (line 1) | type I18nPluginDefs = {
FILE: src/node/types/LegacyParams.ts
type LegacyParams (line 1) | type LegacyParams = {
FILE: src/node/types/MapType.ts
type MapType (line 1) | type MapType = {
type MapArrayType (line 5) | type MapArrayType<T> = {
FILE: src/node/types/PackageInfo.ts
type PackageInfo (line 1) | type PackageInfo = {
type PackageData (line 17) | type PackageData = {
FILE: src/node/types/PadSearchQuery.ts
type PadSearchQuery (line 1) | type PadSearchQuery = {
type PadQueryResult (line 10) | type PadQueryResult = {
FILE: src/node/types/PadType.ts
type PadType (line 4) | type PadType = {
type PadRange (line 26) | type PadRange = {
type APool (line 32) | type APool = {
type AText (line 43) | type AText = {
type PadAuthor (line 49) | type PadAuthor = {
type AChangeSet (line 53) | type AChangeSet = {
FILE: src/node/types/PartType.ts
type PartType (line 1) | type PartType = {
type PluginDef (line 6) | type PluginDef = {
FILE: src/node/types/Plugin.ts
type PluginType (line 4) | type PluginType = {
FILE: src/node/types/PromiseWithStd.ts
type PromiseWithStd (line 4) | type PromiseWithStd = {
FILE: src/node/types/QueryType.ts
type QueryType (line 1) | type QueryType = {
FILE: src/node/types/Revision.ts
type Revision (line 3) | type Revision = {
FILE: src/node/types/RunCMDOptions.ts
type RunCMDOptions (line 1) | type RunCMDOptions = {
type RunCMDPromise (line 7) | type RunCMDPromise = {
type ErrorExtended (line 12) | type ErrorExtended = {
FILE: src/node/types/SecretRotatorType.ts
type SecretRotatorType (line 1) | type SecretRotatorType = {
FILE: src/node/types/SettingsUser.ts
type SettingsUser (line 1) | type SettingsUser = {
FILE: src/node/types/SocketAcknowledge.ts
type SocketAcknowledge (line 1) | type SocketAcknowledge = {
FILE: src/node/types/SocketClientRequest.ts
type SocketClientRequest (line 1) | type SocketClientRequest = {
type PadUserInfo (line 14) | type PadUserInfo = {
type ChangesetRequest (line 24) | type ChangesetRequest = {
FILE: src/node/types/SocketModule.ts
type SocketModule (line 1) | type SocketModule = {
FILE: src/node/types/SwaggerUIResource.ts
type SwaggerUIResource (line 1) | type SwaggerUIResource = {
type OpenAPISuccessResponse (line 13) | type OpenAPISuccessResponse = {
type OpenAPIOperations (line 32) | type OpenAPIOperations = {
FILE: src/node/types/UserSettingsObject.ts
type UserSettingsObject (line 1) | type UserSettingsObject = {
FILE: src/node/types/WebAccessTypes.ts
type WebAccessTypes (line 3) | type WebAccessTypes = {
FILE: src/node/utils/ExportHelper.ts
type LineModel (line 51) | type LineModel = {
FILE: src/node/utils/ExportHtml.ts
type openList (line 306) | type openList = {
FILE: src/node/utils/Minify.ts
constant ROOT_DIR (line 36) | const ROOT_DIR = path.join(settings.root, 'src/static/');
constant LIBRARY_WHITELIST (line 39) | const LIBRARY_WHITELIST = [
FILE: src/node/utils/Settings.ts
type SettingsType (line 159) | type SettingsType = {
FILE: src/node/utils/SettingsTree.ts
class SettingsTree (line 3) | class SettingsTree {
method constructor (line 5) | constructor() {
method addChild (line 9) | public addChild(key: string, value: string) {
method removeChild (line 13) | public removeChild(key: string) {
method getChild (line 17) | public getChild(key: string) {
method hasChild (line 21) | public hasChild(key: string) {
class SettingsNode (line 27) | class SettingsNode {
method constructor (line 32) | constructor(key: string, value?: string | number | boolean | null | u...
method addChild (line 38) | public addChild(path: string[], value: string) {
method collectFromLeafsUpwards (line 63) | public collectFromLeafsUpwards() {
method hasChildren (line 101) | public hasChildren() {
method getChild (line 105) | public getChild(key: string) {
method hasChild (line 109) | public hasChild(key: string) {
FILE: src/node/utils/Stream.ts
class Stream (line 7) | class Stream {
method range (line 13) | static range(start: number, end: number) {
method constructor (line 20) | constructor(values: Iterable<any>) {
method batch (line 57) | batch(size: number) {
method buffer (line 106) | buffer(capacity: number) {
method map (line 130) | map(fn:Function) { return new Stream((function* () { // @ts-ignore
method [Symbol.iterator] (line 136) | [Symbol.iterator]() { return this._iter; }
FILE: src/node/utils/UpdateCheck.ts
type Infos (line 9) | type Infos = {
FILE: src/node/utils/customError.ts
class CustomError (line 10) | class CustomError extends Error {
method constructor (line 17) | constructor(message:string, name: string = 'Error') {
FILE: src/node/utils/padDiff.ts
class PadDiff (line 17) | class PadDiff {
method constructor (line 24) | constructor(pad: PadType, fromRev:string, toRev:string) {
method _isClearAuthorship (line 39) | _isClearAuthorship(changeset: any){
method _createClearAuthorship (line 82) | async _createClearAuthorship(rev: any){
method _createClearStartAtext (line 93) | async _createClearStartAtext(rev: any){
method _getChangesetsInBulk (line 105) | async _getChangesetsInBulk(startRev: any, count: any) {
method _addAuthors (line 123) | _addAuthors(authors: PadAuthor[]){
method _createDiffAtext (line 133) | async _createDiffAtext(){
method getHtml (line 187) | async getHtml(){
method getAuthors (line 205) | async getAuthors() {
method _extendChangesetWithAuthor (line 215) | _extendChangesetWithAuthor(changeset: any, author: any, apool: any){
method _createDeletionChangeset (line 242) | _createDeletionChangeset(cs: any, startAText: any, apool: any){
FILE: src/node/utils/promises.ts
class Gate (line 64) | class Gate<T> extends Promise<T> {
method constructor (line 69) | constructor() {
method [Symbol.species] (line 67) | static get [Symbol.species]() { return Promise; }
FILE: src/node/utils/toolbar.ts
type AttributeObj (line 32) | type AttributeObj = {
type ButtonGroupType (line 47) | type ButtonGroupType = {
class ButtonGroup (line 52) | class ButtonGroup {
method constructor (line 55) | constructor() {
method addButton (line 68) | private addButton(button: Button) {
method render (line 73) | render(): string {
class Button (line 92) | class Button {
method constructor (line 96) | constructor(attributes: AttributeObj) {
method load (line 101) | public static load(btnName: string) {
method render (line 115) | render() {
type SelectButtonOptions (line 129) | type SelectButtonOptions = {
class SelectButton (line 135) | class SelectButton extends Button {
method constructor (line 138) | constructor(attrs: AttributeObj) {
method addOption (line 143) | addOption(value: string, text: string, attributes: AttributeObj) {
method select (line 152) | select(attributes: AttributeObj) {
method render (line 165) | render() {
type AttributeSelect (line 176) | type AttributeSelect = {
class Separator (line 182) | class Separator {
method constructor (line 183) | constructor() {
method render (line 186) | public render() {
method registerButton (line 264) | registerButton(buttonName: string, buttonInfo: any) {
method menu (line 278) | menu(buttons: string[][], isReadOnly: boolean, whichMenu: string, page: ...
FILE: src/static/js/AttributeManager.ts
constant DEFAULT_LINE_ATTRIBUTES (line 13) | const DEFAULT_LINE_ATTRIBUTES = ['author', 'lmkr', 'insertorder', 'start'];
method applyChangeset (line 50) | applyChangeset(changeset) {
method setAttributesOnRange (line 67) | setAttributesOnRange(start, end, attribs) {
method _findRowRange (line 98) | _findRowRange(row, start, end) {
method _setAttributesOnRangeByLine (line 127) | _setAttributesOnRangeByLine(row, startCol, endCol, attribs) {
method lineHasMarker (line 139) | lineHasMarker(lineNum) {
method getAttributeOnLine (line 149) | getAttributeOnLine(lineNum, attributeName) {
method getAttributesOnLine (line 162) | getAttributesOnLine(lineNum) {
method getAttributeOnSelection (line 177) | getAttributeOnSelection(attributeName, prevChar) {
method getAttributesOnPosition (line 250) | getAttributesOnPosition(lineNumber, column) {
method getAttributesOnCaret (line 275) | getAttributesOnCaret() {
method setAttributeOnLine (line 286) | setAttributeOnLine(lineNum, attributeName, attributeValue) {
method removeAttributeOnLine (line 316) | removeAttributeOnLine(lineNum, attributeName, attributeValue) {
method toggleAttributeOnLine (line 360) | toggleAttributeOnLine(lineNum, attributeName, attributeValue) {
method hasAttributeOnSelectionOrCaretPosition (line 366) | hasAttributeOnSelectionOrCaretPosition(attributeName) {
FILE: src/static/js/AttributeMap.ts
class AttributeMap (line 26) | class AttributeMap extends Map {
method fromString (line 35) | public static fromString(str: string, pool?: AttributePool|null): Attr...
method constructor (line 42) | constructor(pool?: AttributePool|null) {
method set (line 53) | set(k: string, v: string):this {
method toString (line 60) | toString() {
method update (line 70) | update(entries: Iterable<Attribute>, emptyValueIsDelete: boolean = fal...
method updateFromString (line 90) | updateFromString(str: string, emptyValueIsDelete: boolean = false): At...
FILE: src/static/js/AttributePool.ts
class AttributePool (line 59) | class AttributePool {
method constructor (line 68) | constructor() {
method clone (line 107) | clone() {
method putAttrib (line 127) | putAttrib(attrib: Attribute, dontAddIfAbsent = false) {
method getAttrib (line 148) | getAttrib(num: number): Attribute {
method getAttribKey (line 161) | getAttribKey(num: number): string {
method getAttribValue (line 172) | getAttribValue(num: number) {
method eachAttrib (line 184) | eachAttrib(func: (k: string, v: string)=>void) {
method toJsonable (line 198) | toJsonable() {
method fromJsonable (line 214) | fromJsonable(obj: this) {
method check (line 228) | check() {
FILE: src/static/js/Builder.ts
class Builder (line 23) | class Builder {
method constructor (line 29) | constructor(oldLen: number) {
FILE: src/static/js/Changeset.ts
type WirePrep (line 1134) | type WirePrep = {
FILE: src/static/js/ChatMessage.ts
class ChatMessage (line 11) | class ChatMessage {
method fromObject (line 17) | static fromObject(obj: ChatMessage) {
method constructor (line 39) | constructor(text: string | null = null, authorId: string | null = null...
method userId (line 75) | get userId() {
method userId (line 79) | set userId(val) {
method userName (line 90) | get userName() {
method userName (line 94) | set userName(val) {
method toJSON (line 101) | toJSON() {
FILE: src/static/js/MergingOpAssembler.ts
class MergingOpAssembler (line 5) | class MergingOpAssembler {
method constructor (line 10) | constructor() {
FILE: src/static/js/Op.ts
type OpCode (line 3) | type OpCode = ''|'='|'+'|'-';
class Op (line 9) | class Op {
method constructor (line 17) | constructor(opcode:''|'='|'+'|'-' = '') {
method toString (line 72) | toString() {
FILE: src/static/js/OpAssembler.ts
class OpAssembler (line 7) | class OpAssembler {
method constructor (line 9) | constructor() {
FILE: src/static/js/OpIter.ts
class OpIter (line 11) | class OpIter {
method constructor (line 17) | constructor(ops: string) {
method hasNext (line 25) | hasNext(): boolean {
method next (line 38) | next(opOut: Op = new Op()): Op {
FILE: src/static/js/SmartOpAssembler.ts
class SmartOpAssembler (line 26) | class SmartOpAssembler {
method constructor (line 34) | constructor() {
FILE: src/static/js/StringAssembler.ts
class StringAssembler (line 4) | class StringAssembler {
method append (line 12) | append(x: string) {
method toString (line 15) | toString() {
FILE: src/static/js/StringIterator.ts
class StringIterator (line 18) | class StringIterator {
method constructor (line 23) | constructor(str: string) {
FILE: src/static/js/TextLinesMutator.ts
class TextLinesMutator (line 14) | class TextLinesMutator {
method constructor (line 23) | constructor(lines: string[]) {
method _linesGet (line 53) | _linesGet(idx: number) {
method _linesSlice (line 69) | _linesSlice(start: number | undefined, end: number | undefined) {
method _linesLength (line 83) | _linesLength() {
method _enterSplice (line 95) | _enterSplice() {
method _leaveSplice (line 108) | _leaveSplice() {
method _isCurLineInSplice (line 121) | _isCurLineInSplice() {
method _putCurLineInSplice (line 133) | _putCurLineInSplice() {
method skipLines (line 150) | skipLines(L: number, includeInSplice?: any) {
method skip (line 182) | skip(N: number, L: number, includeInSplice?: any) {
method removeLines (line 203) | removeLines(L: number) {
method remove (line 256) | remove(N: number, L: any) {
method insert (line 278) | insert(text: string | any[], L: any) {
method hasMore (line 331) | hasMore() {
method close (line 343) | close() {
FILE: src/static/js/ace2_inner.ts
function Ace2Inner (line 42) | function Ace2Inner(editorInfo, cssManagers) {
FILE: src/static/js/broadcast.ts
method lineToElement (line 66) | lineToElement(line, aline) {
method splice (line 79) | splice(start, numRemoved, ...newLines) {
method get (line 113) | get(i) {
method length (line 117) | length() {
method getActiveAuthors (line 121) | getActiveAuthors() {
method queueUp (line 332) | queueUp(revision, width, callback) {
method loadFromQueue (line 357) | loadFromQueue() {
method handleSocketResponse (line 383) | handleSocketResponse(message) {
method handleMessageFromServer (line 407) | handleMessageFromServer(obj) {
FILE: src/static/js/broadcast_revisions.ts
function Revision (line 30) | function Revision(revNum) {
FILE: src/static/js/chat.ts
method show (line 36) | show() {
method stickToScreen (line 52) | stickToScreen(fromInitialCall) {
method chatAndUsers (line 71) | chatAndUsers(fromInitialCall) {
method hide (line 88) | hide() {
method scrollDown (line 99) | scrollDown(force) {
method send (line 112) | async send() {
method addMessage (line 120) | async addMessage(msg, increment, isHistoryAdd) {
method init (line 225) | init(pad) {
FILE: src/static/js/collab_client.ts
method constructor (line 169) | constructor() {
method enqueue (line 173) | async enqueue(fn) {
method commitDelay (line 488) | set commitDelay(ms) { commitDelay = ms; }
method commitDelay (line 489) | get commitDelay() { return commitDelay; }
FILE: src/static/js/pad.ts
class MessageQueue (line 366) | class MessageQueue {
method constructor (line 367) | constructor() {
method setCollabClient (line 372) | setCollabClient(cc) {
method enqueue (line 377) | enqueue(...msgs) {
method init (line 410) | init() {
method _afterHandshake (line 425) | _afterHandshake() {
FILE: src/static/js/pad_automatic_reconnect.ts
method nextTry (line 116) | nextTry() {
FILE: src/static/js/pad_cookie.ts
method constructor (line 23) | constructor() {
method init (line 27) | init() {
method readPrefs_ (line 44) | readPrefs_() {
method writePrefs_ (line 54) | writePrefs_(prefs) {
method getPref (line 58) | getPref(prefName) {
method setPref (line 62) | setPref(prefName, value) {
method clear (line 68) | clear() {
FILE: src/static/js/pad_editbar.ts
class ToolbarItem (line 34) | class ToolbarItem {
method constructor (line 35) | constructor(element) {
method getCommand (line 39) | getCommand() {
method getValue (line 43) | getValue() {
method setValue (line 49) | setValue(val) {
method getType (line 55) | getType() {
method isSelect (line 59) | isSelect() {
method isButton (line 63) | isButton() {
method bind (line 67) | bind(callback) {
method constructor (line 128) | constructor() {
method init (line 134) | init() {
method isEnabled (line 176) | isEnabled() { return true; }
method disable (line 177) | disable() {
method enable (line 180) | enable() {
method registerCommand (line 183) | registerCommand(cmd, callback) {
method registerDropdownCommand (line 187) | registerDropdownCommand(cmd, dropdown) {
method registerAceCommand (line 194) | registerAceCommand(cmd, callback) {
method triggerCommand (line 201) | triggerCommand(cmd, item) {
method toggleDropDown (line 209) | toggleDropDown(moduleName, cb = null) {
method setSyncStatus (line 257) | setSyncStatus(status) {
method setEmbedLinks (line 264) | setEmbedLinks() {
method checkAllIconsAreDisplayedInToolbar (line 282) | checkAllIconsAreDisplayedInToolbar() {
method _bodyKeyEvent (line 300) | _bodyKeyEvent(evt) {
method _registerDefaultCommands (line 361) | _registerDefaultCommands() {
FILE: src/static/js/pad_impexp.ts
function cantExport (line 112) | function cantExport() {
FILE: src/static/js/pad_utils.ts
type PadEvent (line 97) | type PadEvent = {
type JQueryNode (line 101) | type JQueryNode = JQuery<HTMLElement>
class PadUtils (line 103) | class PadUtils {
method constructor (line 118) | constructor() {
FILE: src/static/js/pluginfw/LinkInstaller.ts
class LinkInstaller (line 10) | class LinkInstaller {
method constructor (line 19) | constructor() {
method init (line 30) | public async init() {
method installFromPath (line 41) | public async installFromPath(path: string) {
method installFromGitHub (line 47) | public async installFromGitHub(repository: string) {
method installPlugin (line 53) | public async installPlugin(pluginName: string, version?: string) {
method listPlugins (line 65) | public async listPlugins() {
method uninstallPlugin (line 77) | public async uninstallPlugin(pluginName: string) {
method removeSubDependencies (line 87) | private async removeSubDependencies(plugin: IPluginInfo) {
method removeSubDependency (line 95) | private async removeSubDependency(_name: string, dependency:string) {
method uninstallDependency (line 113) | private uninstallDependency(dependency: string) {
method removeSymlink (line 128) | private async removeSymlink(plugin: IPluginInfo) {
method unlinkSubDependencies (line 141) | private async unlinkSubDependencies(plugin: IPluginInfo) {
method unlinkSubDependency (line 149) | private async unlinkSubDependency(plugin: string, dependency: string) {
method addSubDependencies (line 174) | private async addSubDependencies(plugin: IPluginInfo) {
method addSubDependency (line 181) | private async addSubDependency(plugin: string, dependency: string) {
method linkDependency (line 204) | private linkDependency(dependency: string) {
method unlinkDependency (line 222) | private unlinkDependency(dependency: string) {
method checkLinkedDependencies (line 234) | private async checkLinkedDependencies(plugin: IPluginInfo) {
FILE: src/static/js/scroll.ts
class Scroll (line 5) | class Scroll {
method constructor (line 11) | constructor(outerWin: HTMLIFrameElement) {
method scrollWhenCaretIsInTheLastLineOfViewportWhenNecessary (line 21) | scrollWhenCaretIsInTheLastLineOfViewportWhenNecessary(rep: RepModel, i...
method scrollWhenPressArrowKeys (line 41) | scrollWhenPressArrowKeys(arrowUp: boolean, rep: RepModel, innerHeight:...
method _isCaretAtTheBottomOfViewport (line 55) | _isCaretAtTheBottomOfViewport(rep: RepModel) {
method _isLinePartiallyVisibleOnViewport (line 77) | _isLinePartiallyVisibleOnViewport(lineNumber: number, rep: RepModel){
method _getViewPortTopBottom (line 98) | _getViewPortTopBottom() {
method _getEditorPositionTop (line 114) | _getEditorPositionTop() {
method _getPaddingTopAddedWhenPageViewIsEnable (line 120) | _getPaddingTopAddedWhenPageViewIsEnable() {
method _getScrollXY (line 126) | _getScrollXY() {
method getScrollX (line 144) | getScrollX() {
method getScrollY (line 148) | getScrollY () {
method setScrollX (line 152) | setScrollX(x: number) {
method setScrollY (line 156) | setScrollY(y: number) {
method setScrollXY (line 160) | setScrollXY(x: number, y: number) {
method _isCaretAtTheTopOfViewport (line 164) | _isCaretAtTheTopOfViewport(rep: RepModel) {
method _getPixelsRelativeToPercentageOfViewport (line 194) | _getPixelsRelativeToPercentageOfViewport(innerHeight: number, aboveOfV...
method _getPercentageToScroll (line 205) | _getPercentageToScroll(aboveOfViewport: boolean|undefined) {
method _getPixelsToScrollWhenUserPressesArrowUp (line 213) | _getPixelsToScrollWhenUserPressesArrowUp(innerHeight: number) {
method _scrollYPage (line 222) | _scrollYPage(pixelsToScroll: number) {
method _scrollYPageWithoutAnimation (line 231) | _scrollYPageWithoutAnimation(pixelsToScroll: number) {
method _scrollYPageWithAnimation (line 235) | _scrollYPageWithAnimation(pixelsToScroll: number, durationOfAnimationT...
method _triggerScrollWithAnimation (line 249) | _triggerScrollWithAnimation($elem:any, pixelsToScroll: number, duratio...
method scrollNodeVerticallyIntoView (line 262) | scrollNodeVerticallyIntoView(rep: RepModel, innerHeight: number) {
method _partOfRepLineIsOutOfViewport (line 292) | _partOfRepLineIsOutOfViewport(viewportPosition: Position, rep: RepMode...
method _getLineEntryTopBottom (line 302) | _getLineEntryTopBottom(entry: RepNode, destObj?: Position) {
method _arrowUpWasPressedInTheFirstLineOfTheViewport (line 312) | _arrowUpWasPressedInTheFirstLineOfTheViewport(arrowUp: boolean, rep: R...
method getVisibleLineRange (line 317) | getVisibleLineRange(rep: RepModel) {
method getVisibleCharRange (line 332) | getVisibleCharRange(rep: RepModel) {
FILE: src/static/js/skiplist.ts
type Entry (line 27) | type Entry = {
class Node (line 33) | class Node {
method constructor (line 42) | constructor(entry: Entry|null, levels = 0, downSkips: number|null = 1,...
method propagateWidthChange (line 52) | propagateWidthChange() {
class Point (line 73) | class Point {
method constructor (line 80) | constructor(skipList: SkipList, loc: number) {
method toString (line 113) | toString() {
method insert (line 117) | insert(entry: Entry) {
method delete (line 173) | delete() {
method getNode (line 196) | getNode() {
class SkipList (line 205) | class SkipList {
method constructor (line 212) | constructor() {
method _getNodeAtOffset (line 222) | _getNodeAtOffset(targetOffset: number) {
method _getNodeIndex (line 240) | _getNodeIndex(node: Node, byWidth?: boolean) {
method totalWidth (line 252) | totalWidth() { return this._totalWidth; }
method search (line 257) | search(entryFunc: Function) {
method length (line 280) | length() { return this.keyToNodeMap.size; }
method atIndex (line 282) | atIndex(i: number) {
method splice (line 289) | splice(start: number, deleteCount: number, newEntryArray: Entry[]) {
method next (line 306) | next(entry: Entry) { return this.keyToNodeMap.get(entry.key)!.downPtrs...
method prev (line 307) | prev(entry: Entry) { return this.keyToNodeMap.get(entry.key)!.upPtrs[0...
method push (line 308) | push(entry: Entry) { this.splice(this.keyToNodeMap.size, 0, [entry]); }
method slice (line 310) | slice(start: number, end: number) {
method atKey (line 332) | atKey(key: string) { return this.keyToNodeMap.get(key)!.entry; }
method indexOfKey (line 333) | indexOfKey(key: string) { return this._getNodeIndex(this.keyToNodeMap....
method indexOfEntry (line 334) | indexOfEntry(entry: Entry) { return this.indexOfKey(entry.key); }
method containsKey (line 335) | containsKey(key: string) { return this.keyToNodeMap.has(key); }
method atOffset (line 337) | atOffset(offset: number) { return this._getNodeAtOffset(offset)!.entry; }
method keyAtOffset (line 338) | keyAtOffset(offset: number) { return this.atOffset(offset)!.key; }
method offsetOfKey (line 339) | offsetOfKey(key: string) { return this._getNodeIndex(this.keyToNodeMap...
method offsetOfEntry (line 340) | offsetOfEntry(entry: Entry) { return this.offsetOfKey(entry.key); }
method setEntryWidth (line 341) | setEntryWidth(entry: Entry, width: number) {
method offsetOfIndex (line 345) | offsetOfIndex(i: number) {
method indexOfOffset (line 350) | indexOfOffset(offset: number) {
FILE: src/static/js/types/AText.ts
type AText (line 1) | type AText = {
FILE: src/static/js/types/Attribute.ts
type Attribute (line 1) | type Attribute = [string, string]
FILE: src/static/js/types/ChangeSet.ts
type ChangeSet (line 1) | type ChangeSet = {
FILE: src/static/js/types/ChangeSetBuilder.ts
type ChangeSetBuilder (line 4) | type ChangeSetBuilder = {
FILE: src/static/js/types/PadRevision.ts
type PadRevision (line 1) | type PadRevision = {
FILE: src/static/js/types/RepModel.ts
type RepModel (line 1) | type RepModel = {
type Position (line 13) | type Position = {
type RepNode (line 19) | type RepNode = {
type WindowElementWithScrolling (line 28) | type WindowElementWithScrolling = HTMLIFrameElement & {
FILE: src/static/js/types/SocketIOMessage.ts
type Part (line 8) | type Part = {
type MappedPlugin (line 18) | type MappedPlugin = Part& {
type SocketIOMessage (line 23) | type SocketIOMessage = {
type HistoricalAuthorData (line 28) | type HistoricalAuthorData = MapArrayType<{
type ServerVar (line 34) | type ServerVar = {
type AttributePoolWire (line 47) | type AttributePoolWire = {numToAttrib: {[p: number]: [string, string]}, ...
type UserInfo (line 50) | type UserInfo = {
type ClientVarPayload (line 56) | type ClientVarPayload = {
type ClientVarData (line 108) | type ClientVarData = {
type ClientNewChanges (line 113) | type ClientNewChanges = {
type ClientAcceptCommitMessage (line 122) | type ClientAcceptCommitMessage = {
type ClientConnectMessage (line 127) | type ClientConnectMessage = {
type UserNewInfoMessage (line 138) | type UserNewInfoMessage = {
type UserLeaveMessage (line 145) | type UserLeaveMessage = {
type ClientMessageMessage (line 152) | type ClientMessageMessage = {
type ChatMessageMessage (line 157) | type ChatMessageMessage = {
type ChatMessageMessages (line 164) | type ChatMessageMessages = {
type ClientUserChangesMessage (line 169) | type ClientUserChangesMessage = {
type ClientSendMessages (line 178) | type ClientSendMessages = ClientUserChangesMessage |ClientReadyMessage|...
type ClientReadyMessage (line 180) | type ClientReadyMessage = {
type ClientSaveRevisionMessage (line 191) | type ClientSaveRevisionMessage = {
type PadDeleteMessage (line 196) | type PadDeleteMessage = {
type GetChatMessageMessage (line 203) | type GetChatMessageMessage = {
type ClientSendUserInfoUpdate (line 209) | type ClientSendUserInfoUpdate = {
type ClientSuggestUserName (line 214) | type ClientSuggestUserName = {
type NewRevisionListMessage (line 224) | type NewRevisionListMessage = {
type RevisionLabel (line 229) | type RevisionLabel = {
type PadOptionsMessage (line 234) | type PadOptionsMessage = {
type PadOption (line 240) | type PadOption = {
type SharedMessageType (line 256) | type SharedMessageType = {
type x (line 262) | type x = {
type ClientDisconnectedMessage (line 266) | type ClientDisconnectedMessage = {
type UserChanges (line 271) | type UserChanges = {
type UserSuggestUserName (line 275) | type UserSuggestUserName = {
type ChangesetRequestMessage (line 281) | type ChangesetRequestMessage = {
type CollabroomMessage (line 292) | type CollabroomMessage = {
type ClientVarMessage (line 297) | type ClientVarMessage = | ClientVarData | ClientDisconnectedMessage | C...
type CustomMessage (line 300) | type CustomMessage = {
type ClientCustomMessage (line 305) | type ClientCustomMessage = {
type SocketClientReadyMessage (line 312) | type SocketClientReadyMessage = {
FILE: src/static/js/vendors/browser.ts
function detect (line 23) | function detect(ua) {
FILE: src/static/js/vendors/farbtastic.ts
function calculateMask (line 232) | function calculateMask(sizex, sizey, outputPixel) {
function x (line 458) | function x(i) {
function x (line 464) | function x(i) {
FILE: src/static/js/vendors/html10n.ts
type PluralFunc (line 4) | type PluralFunc = (n: number) => string
class Html10n (line 6) | class Html10n {
method constructor (line 15) | constructor() {
method bind (line 53) | bind(event: string, fct: Func) {
method getPluralRules (line 72) | getPluralRules(lang: string): PluralFunc {
method getTranslatableChildren (line 470) | getTranslatableChildren(element: HTMLElement) {
method localize (line 474) | localize(langs: (string|undefined)[]|string) {
method translateElement (line 498) | translateElement(translations: Map<string, any>, element?: HTMLElement) {
method asyncForEach (line 510) | asyncForEach(list: (string|undefined)[], iterator: any, cb: Function) {
method build (line 526) | build(langs: (string|undefined)[], cb: Function) {
method getLanguage (line 585) | getLanguage() {
method getDirection (line 592) | getDirection() {
method index (line 602) | index() {
method translateNode (line 615) | translateNode(translations: Map<string, any>, node: HTMLElement) {
method get (line 688) | get(id: string, args?:any) {
method substMacros (line 704) | substMacros(key: string, str:string, args:any) {
method substArguments (line 741) | substArguments(str: string, args:any) {
class MicroEvent (line 769) | class MicroEvent {
method constructor (line 772) | constructor() {
method bind (line 776) | bind(event: string, fct: Func) {
method unbind (line 784) | unbind(event: string, fct: Func) {
method trigger (line 795) | trigger(event: string, ...args: any[]) {
method mixin (line 805) | mixin(destObject: any) {
type LoaderFunc (line 816) | type LoaderFunc = () => void
type ErrorFunc (line 818) | type ErrorFunc = (data?:any)=>void
class Loader (line 820) | class Loader {
method constructor (line 825) | constructor(resources: any) {
method load (line 831) | load(lang: string, callback: LoaderFunc) {
method fetch (line 851) | fetch(href: string, lang: string, callback: ErrorFunc) {
method parse (line 879) | parse(lang: string, href: string, data: {
FILE: src/static/js/vendors/jquery.ts
function DOMEval (line 105) | function DOMEval( code, node, doc ) {
function toType (line 135) | function toType( obj ) {
function isArrayLike (line 547) | function isArrayLike( obj ) {
function nodeName (line 565) | function nodeName( elem, name ) {
function fcssescape (line 611) | function fcssescape( ch, asCodePoint ) {
function safeActiveElement (line 778) | function safeActiveElement() {
function find (line 806) | function find( selector, context, results, seed ) {
function createCache (line 949) | function createCache() {
function markFunction (line 970) | function markFunction( fn ) {
function assert (line 979) | function assert( fn ) {
function createInputPseudo (line 1002) | function createInputPseudo( type ) {
function createButtonPseudo (line 1012) | function createButtonPseudo( type ) {
function createDisabledPseudo (line 1023) | function createDisabledPseudo( disabled ) {
function createPositionalPseudo (line 1078) | function createPositionalPseudo( fn ) {
function testContext (line 1101) | function testContext( context ) {
function setDocument (line 1110) | function setDocument( node ) {
function setFilters (line 2084) | function setFilters() {}
function tokenize (line 2088) | function tokenize( selector, parseOnly ) {
function toSelector (line 2160) | function toSelector( tokens ) {
function addCombinator (line 2170) | function addCombinator( matcher, combinator, base ) {
function elementMatcher (line 2232) | function elementMatcher( matchers ) {
function multipleContexts (line 2246) | function multipleContexts( selector, contexts, results ) {
function condense (line 2255) | function condense( unmatched, map, filter, context, xml ) {
function setMatcher (line 2276) | function setMatcher( preFilter, selector, matcher, postFilter, postFinde...
function matcherFromTokens (line 2375) | function matcherFromTokens( tokens ) {
function matcherFromGroupMatchers (line 2443) | function matcherFromGroupMatchers( elementMatchers, setMatchers ) {
function compile (line 2569) | function compile( selector, match /* Internal Use Only */ ) {
function select (line 2610) | function select( selector, context, results, seed ) {
function winnow (line 2763) | function winnow( elements, qualifier, not ) {
function sibling (line 3058) | function sibling( cur, dir ) {
function createOptions (line 3151) | function createOptions( options ) {
function Identity (line 3376) | function Identity( v ) {
function Thrower (line 3379) | function Thrower( ex ) {
function adoptValue (line 3383) | function adoptValue( value, resolve, reject, noValue ) {
function resolve (line 3476) | function resolve( depth, deferred, handler, special ) {
function completed (line 3852) | function completed() {
function fcamelCase (line 3947) | function fcamelCase( _all, letter ) {
function camelCase (line 3954) | function camelCase( string ) {
function Data (line 3971) | function Data() {
function getData (line 4140) | function getData( data ) {
function dataAttr (line 4165) | function dataAttr( elem, key, data ) {
function adjustCSS (line 4477) | function adjustCSS( elem, prop, valueParts, tween ) {
function getDefaultDisplay (line 4545) | function getDefaultDisplay( elem ) {
function showHide (line 4568) | function showHide( elements, show ) {
function getAll (line 4700) | function getAll( context, tag ) {
function setGlobalEval (line 4725) | function setGlobalEval( elems, refElements ) {
function buildFragment (line 4741) | function buildFragment( elems, context, scripts, selection, ignored ) {
function returnTrue (line 4833) | function returnTrue() {
function returnFalse (line 4837) | function returnFalse() {
function on (line 4841) | function on( elem, types, selector, data, fn, one ) {
function leverageNative (line 5329) | function leverageNative( el, type, isSetup ) {
function focusMappedHandler (line 5542) | function focusMappedHandler( nativeEvent ) {
function manipulationTarget (line 5794) | function manipulationTarget( elem, content ) {
function disableScript (line 5805) | function disableScript( elem ) {
function restoreScript (line 5809) | function restoreScript( elem ) {
function cloneCopyEvent (line 5819) | function cloneCopyEvent( src, dest ) {
function fixInput (line 5852) | function fixInput( src, dest ) {
function domManip (line 5865) | function domManip( collection, args, callback, ignored ) {
function remove (line 5963) | function remove( elem, selector, keepData ) {
function computeStyleTests (line 6281) | function computeStyleTests() {
function roundPixelMeasures (line 6325) | function roundPixelMeasures( measure ) {
function curCSS (line 6418) | function curCSS( elem, name, computed ) {
function addGetHookIf (line 6503) | function addGetHookIf( conditionFn, hookFn ) {
function vendorPropName (line 6528) | function vendorPropName( name ) {
function finalPropName (line 6543) | function finalPropName( name ) {
function setPositiveNumber (line 6568) | function setPositiveNumber( _elem, value, subtract ) {
function boxModelAdjustment (line 6580) | function boxModelAdjustment( elem, dimension, box, isBorderBox, styles, ...
function getWidthOrHeight (line 6651) | function getWidthOrHeight( elem, dimension, extra ) {
function Tween (line 7036) | function Tween( elem, options, prop, end, easing ) {
function schedule (line 7159) | function schedule() {
function createFxNow (line 7172) | function createFxNow() {
function genFx (line 7180) | function genFx( type, includeWidth ) {
function createTween (line 7200) | function createTween( value, prop, animation ) {
function defaultPrefilter (line 7214) | function defaultPrefilter( elem, props, opts ) {
function propFilter (line 7386) | function propFilter( props, specialEasing ) {
function Animation (line 7423) | function Animation( elem, properties, options ) {
function stripAndCollapse (line 8137) | function stripAndCollapse( value ) {
function getClass (line 8143) | function getClass( elem ) {
function classesToArray (line 8147) | function classesToArray( value ) {
function buildParams (line 8726) | function buildParams( prefix, obj, traditional, add ) {
function addToPrefiltersOrTransports (line 8879) | function addToPrefiltersOrTransports( structure ) {
function inspectPrefiltersOrTransports (line 8913) | function inspectPrefiltersOrTransports( structure, options, originalOpti...
function ajaxExtend (line 8942) | function ajaxExtend( target, src ) {
function ajaxHandleResponses (line 8962) | function ajaxHandleResponses( s, jqXHR, responses ) {
function ajaxConvert (line 9020) | function ajaxConvert( s, response, jqXHR, isSuccess ) {
function done (line 9536) | function done( status, nativeStatusText, responses, headers ) {
FILE: src/static/js/vendors/nice-select.ts
function create_nice_select (line 61) | function create_nice_select($select) {
FILE: src/static/js/welcome.ts
function getCookie (line 3) | function getCookie(name: string) {
function handleTransferOfSession (line 12) | function handleTransferOfSession() {
FILE: src/static/skins/colibris/pad.js
constant MAX_PADS_IN_HISTORY (line 3) | const MAX_PADS_IN_HISTORY = 3;
FILE: src/tests/backend-new/specs/easysync-assembler.ts
method hasNext (line 157) | hasNext() { return !this._n.done; }
method next (line 158) | next() { const v = this._n.value; this._n = ops.next(); return v as Op; }
FILE: src/tests/backend-new/specs/promises.ts
type TestPromise (line 8) | type TestPromise = {
FILE: src/tests/backend/fuzzImportTest.ts
function runTest (line 30) | async function runTest(number: number) {
function makeid (line 64) | function makeid() {
FILE: src/tests/backend/specs/SecretRotator.ts
class FakeClock (line 23) | class FakeClock {
method constructor (line 29) | constructor() {
method _next (line 37) | _next() { return Math.min(...[...this.timeouts.values()].map((x) => x....
method setNow (line 38) | async setNow(t: number) {
method advance (line 51) | async advance(t: number) { await this.setNow(this._now + t); }
method advanceToNext (line 52) | async advanceToNext() {
method _fire (line 57) | async _fire() {
method now (line 76) | get now() { return this._now; }
method setTimeout (line 77) | setTimeout(fn:Function, wait = 0) {
method clearTimeout (line 84) | clearTimeout(id:number) { this.timeouts.delete(id); }
FILE: src/tests/backend/specs/SessionStore.ts
type Session (line 9) | type Session = {
FILE: src/tests/backend/specs/Stream.ts
class DemoIterable (line 6) | class DemoIterable {
method constructor (line 10) | constructor() {
method completed (line 16) | completed() { return this.errs.length > 0 || this.rets.length > 0; }
method next (line 18) | next() {
method throw (line 23) | throw(err: any) {
method return (line 30) | return(ret: number) {
method [Symbol.iterator] (line 37) | [Symbol.iterator]() { return this; }
FILE: src/tests/backend/specs/api/characterEncoding.ts
function makeid (line 93) | function makeid() {
FILE: src/tests/backend/specs/api/chat.ts
function makeid (line 118) | function makeid() {
FILE: src/tests/backend/specs/api/importexport.ts
function makeid (line 280) | function makeid() {
FILE: src/tests/backend/specs/api/importexportGetPost.ts
function makeid (line 797) | function makeid() {
FILE: src/tests/backend/specs/api/pad.ts
function makeid (line 749) | function makeid() {
function generateLongText (line 759) | function generateLongText() {
FILE: src/tests/backend/specs/api/sessionsAndGroups.ts
function makeid (line 426) | function makeid() {
FILE: src/tests/backend/specs/chat.ts
type CheckFN (line 15) | type CheckFN = ({message, pad, padId}:{
type Message (line 141) | type Message = {
FILE: src/tests/backend/specs/hooks.ts
type ExtendedConsole (line 10) | interface ExtendedConsole extends Console {
FILE: src/tests/backend/specs/socketio.ts
method setSocketIO (line 329) | setSocketIO(io:any) {}
method handleConnect (line 330) | handleConnect(socket:any) {}
method handleDisconnect (line 331) | handleDisconnect(socket:any) {}
method handleMessage (line 332) | handleMessage(socket:any, message:string) {}
method setSocketIO (line 343) | setSocketIO(io:any) { ioServer = io; }
method handleConnect (line 351) | handleConnect(socket:any) { serverSocket = socket; }
method handleConnect (line 364) | handleConnect(socket:any) {
method handleDisconnect (line 368) | handleDisconnect(socket:any) {
method handleConnect (line 392) | handleConnect(socket:any) { serverSocket = socket; }
method handleMessage (line 393) | handleMessage(socket:any, message:string) { assert.equal(socket, serverS...
method handleMessage (line 396) | handleMessage(socket:any, message:any) { assert.fail('wrong handler call...
method constructor (line 405) | constructor(name: string, ...args:any) { super(...args); this.name = nam...
method handleMessage (line 417) | handleMessage(socket:any, msg:any) { return want; }
method constructor (line 426) | constructor() { super('injected test error'); this.name = 'InjectedError...
method handleMessage (line 429) | handleMessage(socket:any, msg:any) { throw new InjectedError(); }
FILE: src/tests/backend/specs/webaccess.ts
method constructor (line 149) | constructor(hookName:string, suffix: string) {
method handle (line 156) | handle(hookName: string, context: any, cb:Function) {
method constructor (line 468) | constructor(hookName: string) {
method handle (line 473) | handle(hookName: string, context:any, cb: Function) {
FILE: src/tests/container/loadSettings.js
function loadSettings (line 18) | function loadSettings() {
FILE: src/tests/frontend/helper/multipleUsers.ts
method init (line 24) | async init() {
method performAsOtherUser (line 41) | async performAsOtherUser(action) {
method close (line 47) | close() {
method _loadJQueryForUser1Frame (line 52) | async _loadJQueryForUser1Frame() {
method _createUser1Frame (line 65) | async _createUser1Frame() {
FILE: src/tests/frontend/runner.js
method define (line 210) | define(...args) {
FILE: src/tests/frontend/specs/importindents.js
method error (line 39) | error(res) {
Condensed preview — 749 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (4,435K chars).
[
{
"path": ".dockerignore",
"chars": 515,
"preview": "*~\n.dockerignore\n.hg\nDockerfile\n\n# Remove the git objects, logs, etc. to make final image smaller.\n# Some files still ne"
},
{
"path": ".editorconfig",
"chars": 292,
"preview": "root = true\n\n[*]\nindent_style = space\nindent_size = 2\ncharset = utf-8\ntrim_trailing_whitespace = true\ninsert_final_newli"
},
{
"path": ".gitattributes",
"chars": 19,
"preview": "* text=auto eol=lf\n"
},
{
"path": ".github/FUNDING.yml",
"chars": 61,
"preview": "# These are supported funding model platforms\n\ngithub: ether\n"
},
{
"path": ".github/ISSUE_TEMPLATE/bug_report.md",
"chars": 1207,
"preview": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n<!-- IMPORTANT: "
},
{
"path": ".github/ISSUE_TEMPLATE/feature_request.md",
"chars": 845,
"preview": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: Feature Request\nassignees: ''\n\n---\n\n"
},
{
"path": ".github/ISSUE_TEMPLATE/plugin-request-template.md",
"chars": 740,
"preview": "---\nname: Plugin request template\nabout: Suggest a plugin for Etherpad\ntitle: ''\nlabels: Plugin Request\nassignees: JohnM"
},
{
"path": ".github/ISSUE_TEMPLATE/security-issue.md",
"chars": 222,
"preview": "---\nname: Security issue\nabout: Notify the Etherpad foundation of a Security issue\ntitle: ''\nlabels: security\nassignees:"
},
{
"path": ".github/ISSUE_TEMPLATE/security.md",
"chars": 331,
"preview": "* * *\n\nname: Security notification\nabout: Disclose a security issue in Etherpad\ntitle: ''\nlabels: security\nassignees: \n\n"
},
{
"path": ".github/PULL_REQUEST_TEMPLATE.md",
"chars": 658,
"preview": "<!--\n\n1. If you haven't already, please read https://github.com/ether/etherpad-lite/blob/master/CONTRIBUTING.md#pull-req"
},
{
"path": ".github/dependabot.yml",
"chars": 480,
"preview": "version: 2\nupdates:\n # Maintain dependencies for GitHub Actions\n - package-ecosystem: \"github-actions\"\n directory: "
},
{
"path": ".github/workflows/backend-tests.yml",
"chars": 9591,
"preview": "name: \"Backend tests\"\n\n# any branch is useful for testing before a PR is submitted\non:\n push:\n paths-ignore:\n -"
},
{
"path": ".github/workflows/build-and-deploy-docs.yml",
"chars": 2076,
"preview": "# Workflow for deploying static content to GitHub Pages\nname: Deploy Docs to GitHub Pages\n\non:\n # Runs on pushes target"
},
{
"path": ".github/workflows/codeql-analysis.yml",
"chars": 1324,
"preview": "name: \"CodeQL\"\n\non:\n push:\n branches: [develop, master]\n pull_request:\n # The branches below must be a subset of"
},
{
"path": ".github/workflows/dependency-review.yml",
"chars": 884,
"preview": "# Dependency Review Action\n#\n# This Action will scan dependency manifest files that change as part of a Pull Reqest, sur"
},
{
"path": ".github/workflows/docker.yml",
"chars": 4427,
"preview": "name: \"Docker\"\non:\n pull_request:\n paths-ignore:\n - 'doc/**'\n push:\n branches:\n - 'develop'\n paths-"
},
{
"path": ".github/workflows/frontend-admin-tests.yml",
"chars": 5892,
"preview": "# Leave the powered by Sauce Labs bit in as this means we get additional concurrency\nname: \"Frontend admin tests\"\n\non:\n "
},
{
"path": ".github/workflows/frontend-tests.yml",
"chars": 6699,
"preview": "# Leave the powered by Sauce Labs bit in as this means we get additional concurrency\nname: \"Frontend tests powered by Sa"
},
{
"path": ".github/workflows/handleRelease.yml",
"chars": 1749,
"preview": "name: \"Handle release\"\n\n\non:\n push:\n tags:\n - 'v*.*.*'\n # allow manual triggering of the workflow\n workflow_d"
},
{
"path": ".github/workflows/load-test.yml",
"chars": 5399,
"preview": "name: \"Loadtest\"\n\n# any branch is useful for testing before a PR is submitted\non:\n push:\n paths-ignore:\n - \"doc"
},
{
"path": ".github/workflows/perform-type-check.yml",
"chars": 1321,
"preview": "name: \"Perform type checks\"\n\n# any branch is useful for testing before a PR is submitted\non:\n push:\n paths-ignore:\n "
},
{
"path": ".github/workflows/rate-limit.yml",
"chars": 2170,
"preview": "name: \"rate limit\"\n\n# any branch is useful for testing before a PR is submitted\non:\n push:\n paths-ignore:\n - \"d"
},
{
"path": ".github/workflows/release.yml",
"chars": 2571,
"preview": "permissions:\n contents: read\nname: Release etherpad\non:\n workflow_dispatch:\n inputs:\n release_type:\n de"
},
{
"path": ".github/workflows/releaseEtherpad.yml",
"chars": 1341,
"preview": "name: releaseEtherpad.yaml\npermissions:\n contents: read\non:\n workflow_dispatch:\n\nenv:\n PNPM_HOME: ~/.pnpm-store\n\njobs"
},
{
"path": ".github/workflows/stale.yml",
"chars": 599,
"preview": "name: 'Close stale issues and PRs'\non:\n schedule:\n - cron: '30 6 * * *'\npermissions:\n issues: write\n pull-requests"
},
{
"path": ".github/workflows/upgrade-from-latest-release.yml",
"chars": 3659,
"preview": "name: \"Upgrade from latest release\"\n\n# any branch is useful for testing before a PR is submitted\non:\n push:\n paths-i"
},
{
"path": ".gitignore",
"chars": 433,
"preview": "/etherpad-win.exe\n/etherpad-win.zip\nnode_modules\n/settings.json\n!settings.json.template\nAPIKEY.txt\nSESSIONKEY.txt\nvar/di"
},
{
"path": ".lgtm.yml",
"chars": 132,
"preview": "extraction:\n javascript:\n index:\n exclude:\n - src/static/js/vendors\n python:\n index:\n exclude:\n"
},
{
"path": ".pr_agent.toml",
"chars": 76,
"preview": "[pr_reviewer]\nrun_on_pr_sync = true\n\n[pr_description]\nrun_on_pr_sync = true\n"
},
{
"path": ".travis.yml",
"chars": 5372,
"preview": "language: node_js\n\nnode_js:\n - \"lts/*\"\n\nservices:\n - docker\n\ncache: false\n\nenv:\n global:\n - secure: \"WMGxFkOeTTlhW"
},
{
"path": "AGENTS.MD",
"chars": 3136,
"preview": "# Agent Guide - Etherpad\n\nWelcome to the Etherpad project. This guide provides essential context and instructions for AI"
},
{
"path": "CHANGELOG.md",
"chars": 74409,
"preview": "# 2.6.1\n\nFor those wondering where the new updates are and why it was very quite throughout the last 1 1/2 years – I've "
},
{
"path": "CONTRIBUTING.md",
"chars": 9030,
"preview": "# Contributor Guidelines\n(Please talk to people on the mailing list before you change this page, see our section on [how"
},
{
"path": "Dockerfile",
"chars": 5897,
"preview": "# Etherpad Lite Dockerfile\n#\n# https://github.com/ether/etherpad-lite\n#\n# Author: muxator\nARG BUILD_ENV=git\n\nARG PnpmVer"
},
{
"path": "LICENSE",
"chars": 11353,
"preview": " Apache License\n Version 2.0, January 2004\n "
},
{
"path": "README.md",
"chars": 12184,
"preview": "# Etherpad: A real-time collaborative editor for the web\n\n => {\n if (sortBy === curr"
},
{
"path": "admin/src/utils/useDebounce.ts",
"chars": 512,
"preview": "import {DependencyList, EffectCallback, useMemo, useRef} from \"react\";\nimport {useAnimationFrame} from \"./AnimationFrame"
},
{
"path": "admin/src/utils/utils.ts",
"chars": 2791,
"preview": "export const cleanComments = (json: string|undefined)=>{\n if (json !== undefined){\n json = json.replace(/\\/\\*."
},
{
"path": "admin/src/vite-env.d.ts",
"chars": 88,
"preview": "/// <reference types=\"vite/client\" />\n/// <reference types=\"vite-plugin-svgr/client\" />\n"
},
{
"path": "admin/tsconfig.json",
"chars": 605,
"preview": "{\n \"compilerOptions\": {\n \"target\": \"ES2020\",\n \"useDefineForClassFields\": true,\n \"lib\": [\"ES2020\", \"DOM\", \"DOM."
},
{
"path": "admin/tsconfig.node.json",
"chars": 233,
"preview": "{\n \"compilerOptions\": {\n \"composite\": true,\n \"skipLibCheck\": true,\n \"module\": \"ESNext\",\n \"moduleResolution\""
},
{
"path": "admin/vite.config.ts",
"chars": 836,
"preview": "import { defineConfig } from 'vite'\nimport {viteStaticCopy} from \"vite-plugin-static-copy\";\nimport react from '@vitejs/p"
},
{
"path": "best_practices.md",
"chars": 8357,
"preview": "# Contributor Guidelines\n(Please talk to people on the mailing list before you change this page, see our section on [how"
},
{
"path": "bin/buildDebian.sh",
"chars": 1035,
"preview": "#!/usr/bin/env bash\n\n# IMPORTANT\n# Protect against misspelling a var and rm -rf /\nset -u\nset -e\n\nSRC=/tmp/etherpad-deb-s"
},
{
"path": "bin/buildForWindows.sh",
"chars": 2151,
"preview": "#!/bin/sh\n\nset -e\n\npecho() { printf %s\\\\n \"$*\"; }\nlog() { pecho \"$@\"; }\nerror() { log \"ERROR: $@\" >&2; }\nfatal() { error"
},
{
"path": "bin/checkAllPads.ts",
"chars": 984,
"preview": "'use strict';\n/*\n * This is a debug tool. It checks all revisions for data corruption\n */\nimport process from \"node:proc"
},
{
"path": "bin/checkPad.ts",
"chars": 1035,
"preview": "'use strict';\n/*\n * This is a debug tool. It checks all revisions for data corruption\n */\nimport process from \"node:proc"
},
{
"path": "bin/cleanRun.sh",
"chars": 896,
"preview": "#!/bin/sh\n\n# Move to the Etherpad base directory.\nMY_DIR=$(cd \"${0%/*}\" && pwd -P) || exit 1\ncd \"${MY_DIR}/..\" || exit 1"
},
{
"path": "bin/commonPlugins.ts",
"chars": 765,
"preview": "import {PackageData} from \"ep_etherpad-lite/node/types/PackageInfo\";\nimport {writeFileSync} from \"fs\";\nimport {installed"
},
{
"path": "bin/convertSettings.json.template",
"chars": 158,
"preview": "{\n \"etherpadDB\":\n {\n \"host\": \"localhost\",\n \"port\": 3306,\n \"database\": \"etherpad\",\n \"user\": \"etherpaduser\","
},
{
"path": "bin/createRelease.sh",
"chars": 7918,
"preview": "#!/bin/bash\n#\n# WARNING: since Etherpad 1.7.0 (2018-08-17), this script is DEPRECATED, and\n# will be removed/mo"
},
{
"path": "bin/createUserSession.ts",
"chars": 2237,
"preview": "'use strict';\n\n/*\n * A tool for generating a test user session which can be used for debugging configs\n * that require s"
},
{
"path": "bin/deb-src/DEBIAN/control",
"chars": 208,
"preview": "Package: etherpad\nVersion: 1.3\nSection: base\nPriority: optional\nArchitecture: i386\nInstalled-Size: SIZE\nDepends:\nMaintai"
},
{
"path": "bin/deb-src/DEBIAN/postinst",
"chars": 312,
"preview": "#!/bin/bash\n# Start the services!\n\nservice etherpad start\necho \"Give Etherpad about 3 minutes to install dependencies th"
},
{
"path": "bin/deb-src/DEBIAN/preinst",
"chars": 578,
"preview": "#!/bin/bash\n\n# Installs node if it isn't already installed\n#\n# Don't steamroll over a previously installed node version\n"
},
{
"path": "bin/deb-src/DEBIAN/prerm",
"chars": 65,
"preview": "#!/bin/bash\n\n# Stop the appserver:\nservice etherpad stop || true\n"
},
{
"path": "bin/deb-src/sysroot/etc/init/etherpad.conf",
"chars": 797,
"preview": "description \"etherpad\"\n\nstart on started networking\nstop on runlevel [!2345]\n\nenv EPHOME=/opt/etherpad\nenv EPLOGS=/var/l"
},
{
"path": "bin/debugRun.sh",
"chars": 700,
"preview": "#!/bin/sh\n\n# Move to the Etherpad base directory.\nMY_DIR=$(cd \"${0%/*}\" && pwd -P) || exit 1\ncd \"${MY_DIR}/..\" || exit 1"
},
{
"path": "bin/deleteAllGroupSessions.ts",
"chars": 2035,
"preview": "/*\n* A tool for deleting ALL GROUP sessions Etherpad user sessions from the CLI,\n* because sometimes a brick is required"
},
{
"path": "bin/deletePad.ts",
"chars": 1435,
"preview": "'use strict';\n\n/*\n * A tool for deleting pads from the CLI, because sometimes a brick is required\n * to fix a window.\n *"
},
{
"path": "bin/extractPadData.ts",
"chars": 1925,
"preview": "'use strict';\n\n/*\n * This is a debug tool. It helps to extract all datas of a pad and move it from\n * a productive envir"
},
{
"path": "bin/fastRun.sh",
"chars": 633,
"preview": "#!/bin/bash\n#\n# Run Etherpad directly, assuming all the dependencies are already installed.\n#\n# Useful for developers, o"
},
{
"path": "bin/functions.sh",
"chars": 1967,
"preview": "# minimum required node version\nREQUIRED_NODE_MAJOR=12\nREQUIRED_NODE_MINOR=13\n\n# minimum required npm version\nREQUIRED_N"
},
{
"path": "bin/generateReleaseNotes.ts",
"chars": 889,
"preview": "import {readFileSync} from \"node:fs\";\n\nconst changelog = readFileSync('../CHANGELOG.md')\nconst changelogText = changelog"
},
{
"path": "bin/importSqlFile.ts",
"chars": 2978,
"preview": "'use strict';\n\n// As of v14, Node.js does not exit when there is an unhandled Promise rejection. Convert an\n// unhandled"
},
{
"path": "bin/installDeps.sh",
"chars": 1261,
"preview": "#!/bin/sh\n\n\n# Move to the Etherpad base directory.\nMY_DIR=$(cd \"${0%/*}\" && pwd -P) || exit 1\ncd \"${MY_DIR}/..\" || exit "
},
{
"path": "bin/installLocalPlugins.sh",
"chars": 1326,
"preview": "#!/bin/bash\nset -euo pipefail\nIFS=$'\\n\\t'\n\ntrim() {\n local var=\"$*\"\n # remove leading whitespace characters\n va"
},
{
"path": "bin/installOnWindows.bat",
"chars": 979,
"preview": "@echo off\n\n:: Change directory to etherpad-lite root\ncd /D \"%~dp0\\..\"\n\n:: Is node installed?\ncmd /C node -e \"\" || ( echo"
},
{
"path": "bin/make_docs.ts",
"chars": 1683,
"preview": "import {exec} from 'child_process'\nimport fs from 'fs'\nimport path from 'path'\n\nimport pjson from '../src/package.json'\n"
},
{
"path": "bin/migrateDB.ts",
"chars": 2289,
"preview": "// DB migration\nimport {readFileSync} from 'node:fs'\nimport {Database, DatabaseType} from \"ueberdb2\";\nimport path from \""
},
{
"path": "bin/migrateDirtyDBtoRealDB.ts",
"chars": 2101,
"preview": "'use strict';\n\nimport process from 'node:process';\nimport {Database, DatabaseType} from \"ueberdb2\";\nimport log4js from '"
},
{
"path": "bin/nsis/README.md",
"chars": 164,
"preview": "A simple NSIS script to Install Etherpad (Server) on Windows and start it.\n\n# TODO\n1. i18n\n1. Run as Service\n1. Display "
},
{
"path": "bin/nsis/etherpad.nsi",
"chars": 1498,
"preview": ";Include Modern UI\n!include \"MUI2.nsh\"\n!include x64.nsh\n\n;--------------------------------\n;Styling\n!define MUI_ICON \"br"
},
{
"path": "bin/package.json",
"chars": 1433,
"preview": "{\n \"name\": \"bin\",\n \"version\": \"2.6.1\",\n \"description\": \"\",\n \"main\": \"checkAllPads.js\",\n \"directories\": {\n \"doc\":"
},
{
"path": "bin/plugins/README.md",
"chars": 1287,
"preview": "The files in this folder are for Plugin developers.\n\n# Get suggestions to improve your Plugin\n\nThis code will check your"
},
{
"path": "bin/plugins/checkPlugin.ts",
"chars": 18531,
"preview": "/*\n * Usage -- see README.md\n *\n * Normal usage: node bin/plugins/checkPlugin.js ep_whatever\n * Auto fix "
},
{
"path": "bin/plugins/getCorePlugins.sh",
"chars": 1169,
"preview": "#!/bin/sh\n\nset -e\n\nnewline='\n'\n\npecho () { printf %s\\\\n \"$*\"; }\nlog () { pecho \"$@\"; }\nerror () { log \"ERROR: $@\" >&2; }"
},
{
"path": "bin/plugins/lib/CONTRIBUTING.md",
"chars": 8222,
"preview": "# Contributor Guidelines\n(Please talk to people on the mailing list before you change this page, see our section on [how"
},
{
"path": "bin/plugins/lib/LICENSE",
"chars": 11357,
"preview": " Apache License\n Version 2.0, January 2004\n "
},
{
"path": "bin/plugins/lib/README.md",
"chars": 1267,
"preview": "# [plugin_name]\n\nTODO: Describe the plugin.\n\n## Example animated gif of usage if appropriate\n\n || exit 1\ncd \"${mydir}/../..\"\npdir=$(cd .. && pwd -P) || ex"
},
{
"path": "bin/plugins/reTestAllPlugins.sh",
"chars": 377,
"preview": "echo \"herp\";\nfor dir in `ls node_modules`;\ndo\n echo $dir\n if [[ $dir == *\"ep_\"* ]]; then\n if [[ $dir != \"ep_etherpa"
},
{
"path": "bin/plugins/stalePlugins.ts",
"chars": 752,
"preview": "'use strict';\n\n// Returns a list of stale plugins and their authors email\n\nimport axios from 'axios'\nimport process from"
},
{
"path": "bin/plugins/updateAllPluginsScript.sh",
"chars": 797,
"preview": "cd node_modules\nGHUSER=johnmclear; curl \"https://api.github.com/users/$GHUSER/repos?per_page=1000\" | grep -o 'git@[^\"]*'"
},
{
"path": "bin/plugins/updateCorePlugins.sh",
"chars": 169,
"preview": "#!/bin/sh\n\nset -e\n\nfor dir in node_modules/ep_*; do\n dir=${dir#node_modules/}\n [ \"$dir\" != ep_etherpad-lite ] || conti"
},
{
"path": "bin/plugins.ts",
"chars": 3019,
"preview": "'use strict';\n\nimport {linkInstaller, checkForMigration} from \"ep_etherpad-lite/static/js/pluginfw/installer\";\nimport {p"
},
{
"path": "bin/push-after-release.sh",
"chars": 437,
"preview": "#!/bin/bash\n\n# Specify the path to your package.json file\nPACKAGE_JSON_PATH=\"./src/package.json\"\n\n# Check if the file ex"
},
{
"path": "bin/rebuildPad.ts",
"chars": 3360,
"preview": "/*\n This is a repair tool. It rebuilds an old pad at a new pad location up to a\n known \"good\" revision.\n*/\n\n// As of v"
},
{
"path": "bin/release.ts",
"chars": 8461,
"preview": "'use strict';\n\n// As of v14, Node.js does not exit when there is an unhandled Promise rejection. Convert an\n// unhandled"
},
{
"path": "bin/repairPad.ts",
"chars": 1949,
"preview": "'use strict';\n\nimport process from \"node:process\";\n\n/*\n * This is a repair tool. It extracts all datas of a pad, removes"
},
{
"path": "bin/run.sh",
"chars": 1163,
"preview": "#!/bin/sh\n\n# Move to the Etherpad base directory.\nMY_DIR=$(cd \"${0%/*}\" && pwd -P) || exit 1\ncd \"${MY_DIR}/..\" || exit 1"
},
{
"path": "bin/safeRun.sh",
"chars": 1757,
"preview": "#!/bin/sh\n\n# This script ensures that ep-lite is automatically restarting after\n# an error happens\n\n# Handling Errors\n# "
},
{
"path": "bin/tsconfig.json",
"chars": 12285,
"preview": "{\n \"compilerOptions\": {\n /* Visit https://aka.ms/tsconfig to read more about this file */\n\n /* Projects */\n //"
},
{
"path": "bin/updatePlugins.sh",
"chars": 266,
"preview": "#!/bin/sh\nset -e\nmydir=$(cd \"${0%/*}\" && pwd -P) || exit 1\ncd \"${mydir}\"/..\nOUTDATED=$(npm outdated --depth=0 | awk '{pr"
},
{
"path": "doc/.gitignore",
"chars": 22,
"preview": ".vitepress/cache\ndist\n"
},
{
"path": "doc/.vitepress/config.mts",
"chars": 2611,
"preview": "import { defineConfig } from 'vitepress'\nimport {version} from '../../package.json'\n// https://vitepress.dev/reference/s"
},
{
"path": "doc/.vitepress/theme/components/SvgImage.vue",
"chars": 389,
"preview": "<script setup lang=\"ts\">\ndefineProps<{ svg: string }>()\n</script>\n\n<template>\n <figure class=\"svg-image-root\" v-html=\"s"
},
{
"path": "doc/.vitepress/theme/index.ts",
"chars": 322,
"preview": "import { h } from 'vue'\nimport type { Theme } from 'vitepress'\nimport DefaultTheme from 'vitepress/theme'\nimport './styl"
},
{
"path": "doc/.vitepress/theme/styles/vars.css",
"chars": 2103,
"preview": "/**\n * Colors\n * -------------------------------------------------------------------------- */\n\n:root {\n --vp-c-brand"
},
{
"path": "doc/api/api.adoc",
"chars": 320,
"preview": "include::./embed_parameters.adoc[]\n\ninclude::./http_api.adoc[]\n\ninclude::./hooks_overview.adoc[]\n\ninclude::./hooks_clien"
},
{
"path": "doc/api/changeset_library.adoc",
"chars": 1528,
"preview": "== Changeset Library\n\nThe https://github.com/ether/etherpad-lite/blob/develop/src/static/js/Changeset.ts[changeset\nlibra"
},
{
"path": "doc/api/changeset_library.md",
"chars": 1497,
"preview": "# Changeset Library\n\nThe [changeset\nlibrary](https://github.com/ether/etherpad-lite/blob/develop/src/static/js/Changeset"
},
{
"path": "doc/api/editbar.adoc",
"chars": 935,
"preview": "== Editbar\nsrc/static/js/pad_editbar.js\n\n=== isEnabled()\n\n=== disable()\n\n=== toggleDropDown(dropdown)\nShows the dropdown"
},
{
"path": "doc/api/editbar.md",
"chars": 1098,
"preview": "# Editbar\n\nLocated in `src/static/js/pad_editbar.js`\n\n## isEnabled()\n\nIf the editorbar contains the class `enabledtoolba"
},
{
"path": "doc/api/editorInfo.adoc",
"chars": 3917,
"preview": "== editorInfo\n\n=== editorInfo.ace_replaceRange(start, end, text)\nThis function replaces a range (from `start` to `end`) "
},
{
"path": "doc/api/editorInfo.md",
"chars": 5562,
"preview": "# EditorInfo\n\nLocation: `src/static/js/ace2_inner.js`\n\n## editorInfo.ace_replaceRange(start, end, text)\nThis function re"
},
{
"path": "doc/api/embed_parameters.adoc",
"chars": 1269,
"preview": "== Embed parameters\nYou can easily embed your etherpad-lite into any webpage by using iframes. You can configure the emb"
},
{
"path": "doc/api/embed_parameters.md",
"chars": 1229,
"preview": "# Embed parameters\nYou can easily embed your etherpad-lite into any webpage by using iframes. You can configure the embe"
},
{
"path": "doc/api/hooks_client-side.adoc",
"chars": 17395,
"preview": "== Client-side hooks\n\nMost of these hooks are called during or in order to set up the formatting\nprocess.\n\n=== documentR"
},
{
"path": "doc/api/hooks_client-side.md",
"chars": 17296,
"preview": "# Client-side hooks\n\nMost of these hooks are called during or in order to set up the formatting\nprocess.\n\n## documentRea"
},
{
"path": "doc/api/hooks_overview.adoc",
"chars": 4887,
"preview": "== Hooks\n\nA hook function is registered with a hook via the plugin's `ep.json` file. See\nthe Plugins section for details"
},
{
"path": "doc/api/hooks_overview.md",
"chars": 4876,
"preview": "# Hooks\n\nA hook function is registered with a hook via the plugin's `ep.json` file. See\nthe Plugins section for details."
},
{
"path": "doc/api/hooks_server-side.adoc",
"chars": 38776,
"preview": "== Server-side hooks\nThese hooks are called on server-side.\n\n=== loadSettings\nCalled from: `src/node/server.ts`\n\nThings "
},
{
"path": "doc/api/hooks_server-side.md",
"chars": 38319,
"preview": "# Server-side hooks\n\nThese hooks are called on server-side.\n\n## loadSettings\nCalled from: `src/node/server.js`\n\nThings i"
}
]
// ... and 549 more files (download for full content)
About this extraction
This page contains the full source code of the ether/etherpad-lite GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 749 files (4.0 MB), approximately 1.1M tokens, and a symbol index with 702 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.