Plugin API Draft

Jon Richter edited this page Apr 7, 2014 · 22 revisions

General

Resources

For Developers

How to's

Set up

Advanced steps

Integrating Etherpad in your web app

for Developers

Clone this wiki locally

WARNING: This page is NO LONGER APPLICABLE, please check https://github.com/ether/etherpad-lite/wiki/Plugin-API-Hooks for more helpful information....

This is extremely early work - one might even say speculation - on a proposed plugin API. Please add, delete, +1, or -1.

A working of example of how some of this could be implemented lives at https://github.com/jhollinger/etherpad-lite/tree/plugin_api_ideas. Note node/server.js and the new node/features directory, where much of the core functionality has been implemented like plugins.

The Plugin API should be...

  • The API should be unified (no distinction between server-side and UI plugins)
    • custom js and css will remain, but should not be confused with plugins
  • It should allow easy use of templates, as should some parts of core
  • Plugins should (generally) be npm modules, but we must allow for "proprietary" plugins that aren't published
    • npm plugins should possibly have a prefix - maybe "epl-"?
  • Some existing features (e.g. timeslider, chat, import/export) should be pulled out into plugins, but would still be shipped with core
    • This helps ensure a quality API and gives plugin authors some great examples
    • This implies that plugins should be on the same "level" as core components; they will have deep access to the system
    • This moves EPLite towards a "platform" mentality, rather than "an app with some integration points"
  • Common, tedious things should be made trivial (e.g. serving custom static assets, rendering templates, adding buttons to the editor)
  • "Convention over configuration" - a plugin should be functional (though probably not finished) with but a few lines of code. The author shouldn't even have to require anything up-front.
  • It should use a "global" hooks system
    • i.e. use of hooks should not be isolated to plugins. core should use hooks to build (and obviously execute) as much behavior as possible

Examples of functionality/plugins

  • Monospace button
  • Syntax highlighting
  • A complete Admin UI
  • Integrated OpenID/BrowserID auth
  • Core features (chat, timeslider, import/export)
  • Emailing pad change notifications to authors
  • The colou?r picker - the packaged one could be extended or completely overwritten

Hooks, or server.js pieces which plugins should have direct access to

  • The express app
  • SocketIORouter
  • The async object from server.js (easier if they don't have to require/dependencyify it)
  • All the other plugins?
  • the console, version, root path, server.js.exports.maxAge
  • Minify.js - they should be able to append files to pad.js/timeslider.js, or create new minified chains
  • Hooks for customizing various parts of the pad editor
    • Adding buttons
    • ...
  • Database store access, for creating new types of objects
  • Hooks into pad creation and changes, group creation, author creation, etc.

A contrived example

Litter this with code examples of how you'd like things to work.

npm_modules/
 epl-example/
  index.js (main file)
  lib/ (optional, for use by main file)
   socketio_thing.js
  templates/ (optional, default template location)
   huzzah.ejs
  static/ (optional, default static file location)
   js/
    a.js
    b.js
   css/
    c.css
  package.json

index.js

var plugin = module.exports;

// Required config
plugin.name = 'example';
plugin.version = '1.0';

// Optional config with sensible defaults. These are the defaults.
plugin.templates = 'templates'; // Name of plugin's template dir
plugin.static = 'static'; // Name of plugin's static assets dir

// Optional pre-init hook. Run before any plugins' init hooks.
plugin.preInit = function() {
}

// Optional post-init hook. Run after all plugins' init hooks.
plugin.postInit = function() {
}

// The primary callback, passed the hooks object.
// Right before preInit, the module is assigned a new prototype, which contains the API.
plugin.init = function(on) {
  // Hook for access to the Express app
  on.init.app(function(app) {
    plugin.serveJS(app); // Serves /static/js/ under "/plugins/example/js/"
    plugin.serveCSS(app); // CSS equivalent to above

    app.get('/some/path, function(req, res) {
      plugin.render(res, 'huzzah'); // Calls "res.render" using a plugin's template
    });
  });

  // Hook for access to the socket.io handler
  on.init.socketIO(function(socketIO) {
    var iothing = require('./socketio_thing');
    socketIO.addComponent('example, iothing);
  });

  // Hook for adding minified files
  on.init.minify(function(minify) {
    // Appends "b.js" to the virtual pad.js minified file
    minify.addJS('pad.js', plugin.jsPath('b.js'));
    // Creates a new minified chain called "foo.js" and adds two files from the plugin
    minify.addJS('foo.js', plugin.jsPath('foo1.js'));
    minify.addJS('foo.js', plugin.jsPath('foo2.js'));
  });

  // Add a button to the pad editor. Any more specific ideas?
  on.pad.editor(function(editor) {
    editor.addButton(...
  });

  // Hook for saving a changset
  on.pad.change(function(pad, changeset) {
    // email authors about changeset
  });

  // These (and other) attributes are inherited from the plugin's prototype, which automatically assigned
  plugin.rootPath; // The full filesystem path to the plugin's root dir
  plugin.env.rootPath; // The full filesystem path to the EPL installation
  plugin.env.console.log("Logging!");
}

Other thoughts, concerns

  • Core will need to convert pad.html and timeslider.html to templates. Which engine should we use?
    • EJS - Simple, HTML with embedded JS, should look familiar to most everyone +1 jhollinger +1 Pita +1 fourplusone
    • Jade/HAML - Clean, pseudo-HTML with embedded JS, higher learning curve, somewhat "snooty" in this author's opinion -1 jhollinger
    • JSDOM + Weld - Intriguing, but very high learning curve
    • hogan.js - Looks even simpler then EJS
  • How to ensure that a plugin works with a specific version of etherpad lite?
  • How to enable plugins? Live via an admin interface or in a config file?
  • How can we detect plugins that are jamming up async due to dropped callbacks?
  • We need to find a way to get a list of all etherpad lite plugins in the npm registry.
  • We need some way so etherpad lite can find the the installed plugins.
  • Some plugins will need configs
  • Lots of references to static/js/plugins.js, but it's not obvious (to jhollinger) if they do anything
  • Should there be async as well as sync Hooks?
    • async hooks for thinks like authentication, middleware (and almost everything server side)
    • sync hooks editor related stuff like input validation etc
  • A branch on Pita/etherpad-lite where some code (i.e. parts of https://github.com/jhollinger/etherpad-lite/tree/plugin_api_ideas) could be pulled into/discussed +1 jhollinger