Skip to content
jhollinger edited this page Jan 29, 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.

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() {
  // Add a button to the pad editor which takes user to the route below. Any ideas?
  plugin.addButton(...

  // Add a route to the app
  plugin.env.app.get('/p/:pad/huzzah', function(req, res) {
    res.send(req.params.pad + "! Huzzah!");
  });
}

Examples of functionality/plugins

  • Monospace button
  • Syntax highlighting
  • A complete Admin UI
  • Integrated OpenID/BrowserID auth
  • Core features (chat, timeslider, import/export)

Devil's advocate: Much of the above list is currently possible with custom JS/CSS or the HTTP API. Is a plugin API really necessary?

Response: Custom JS/CSS is great, but it's only for UI changes. The HTTP API is great for integrating EPLite with other applications. But if you want to extend EPLite, writing a custom "front-end" app is often a cumbersome way to go about it.

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 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() {
  plugin.serveJS(); // Everything in static/js will be available under "/plugins/example/js/"
  this.serveCSS(); // Same as above. Yes, plugin == this.
  plugin.addMinifiedJS('pad.js', 'b.js'); // Appends "c.js" to the virtual pad.js minified file

  var root = this.rootPath; // The full filesystem path to the plugin's root dir
  // plugin.env holds useful info and objects from core, such as the express app, socketio handler, etc.
  var eplRoot = this.env.rootPath; // The full filesystem path to the EPL installation
  plugin.env.console.log("Logging!");

  // Direct access to the Express app - it's a great framework, why not?
  plugin.env.app.get('/some/path, function(req, res) {
    // A helper which gives "res.render" the full path to this plugin's templates
    plugin.render(res, 'huzzah');
  });

  // Add a component to the socketio handler
  var iothing = require('./socketio_thing');
  plugin.env.socketIO.addComponent('example, iothing);
}

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

General

Resources

For Developers

How to's

Set up

Advanced steps

Integrating Etherpad in your web app

for Developers

Clone this wiki locally