Skip to content
jhollinger edited this page Feb 2, 2012 · 23 revisions

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

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

A trivial example

This example should present the absolute minimum amount of code needed to create a plugin that does some small thing.

This plugin is an npm module named "epl-huzzah". This is its index.js file.

var plugin = module.exports;

plugin.name = "huzzah";
plugin.version = "1.0";

plugin.init = function(on) {
  // Add a routes to the app
  on.init.app(function(app) {
    app.get('/p/:pad/huzzah', function(req, res) {
      plugin.render(res, 'my_template');
    });
  });
}

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

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
  lib/
   example.js
   socketio_thing.js
  templates/
   huzzah.ejs
  static/
   js/
    a.js
    b.js
   css/
    c.css
  package.json

index.js

module.exports = require('./lib/example.js');

lib/example.js

var plugin = module.exports;

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

// Optional settings 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

// Right before this hook, the plugin is assigned a new prototype, which contains the API
plugin.init = function(on) {
  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
    });
  });

  on.init.socketIO(function(socketIO) {
    var iothing = require('./socketio_thing');
    socketIO.addComponent('example, iothing);
  });

  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(...
  });

  on.pad.change(function(pad, changeset) {
    // email authors about changeset
  });

  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
  • 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
  • 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

General

Resources

For Developers

How to's

Set up

Advanced steps

Integrating Etherpad in your web app

for Developers

Clone this wiki locally