diff --git a/README.md b/README.md index 84e866585..2a30b9708 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ Here is the **[FAQ](https://github.com/Pita/etherpad-lite/wiki/FAQ)** **As root:**
    -
  1. Install the dependencies. We need gzip, git, curl, libssl develop libraries, python and gcc.
    For Debian/Ubuntu apt-get install gzip git-core curl python libssl-dev build-essential
    +
  2. Install the dependencies. We need gzip, git, curl, libssl develop libraries, python and gcc.
    For Debian/Ubuntu apt-get install gzip git-core curl python libssl-dev pkg-config build-essential
    For Fedora/CentOS yum install gzip git-core curl python openssl-devel && yum groupinstall "Development Tools"

  3. Install node.js @@ -73,6 +73,7 @@ Here is the **[FAQ](https://github.com/Pita/etherpad-lite/wiki/FAQ)**
    1. Move to a folder where you want to install Etherpad Lite. Clone the git repository git clone 'git://github.com/Pita/etherpad-lite.git'
       
    2. +
    3. Change into the directory containing the Etherpad Lite source code clone with cd etherpad-lite
    4. Install the dependencies with bin/installDeps.sh
       
    5. Start it with bin/run.sh
       
    6. Open your web browser and visit http://localhost:9001. You like it? Look at the 'Next Steps' section below
    7. diff --git a/README.plugins b/README.plugins new file mode 100644 index 000000000..72c456447 --- /dev/null +++ b/README.plugins @@ -0,0 +1,16 @@ +So, a plugin is an npm package whose name starts with ep_ and that contains a file ep.json +require("ep_etherpad-lite/static/js/plugingfw/plugins").update() will use npm to list all installed modules and read their ep.json files. These will contain registrations for hooks which are loaded +A hook registration is a pairs of a hook name and a function reference (filename for require() plus function name) +require("ep_etherpad-lite/static/js/plugingfw/hooks").callAll("hook_name", {argname:value}) will call all hook functions registered for hook_name +That is the basis. +Ok, so that was a slight simplification: inside ep.json, hook registrations are grouped into groups called "parts". Parts from all plugins are ordered using a topological sort according to "pre" and "post" pointers to other plugins/parts (just like dependencies, but non-installed plugins are silently ignored). +This ordering is honored when you do callAll(hook_name) - hook functions for that hook_name are called in that order +Ordering between plugins is undefined, only parts are ordered. + +A plugin usually has one part, but it van have multiple. +This is so that it can insert some hook registration before that of another plugin, and another one after. +This is important for e.g. registering URL-handlers for the express webserver, if you have some very generic and some very specific url-regexps +So, that's basically it... apart from client-side hooks +which works the same way, but uses a separate member of the part (part.client_hooks vs part.hooks), and where the hook function must obviously reside in a file require():able from the client... +One thing more: The main etherpad tree is actually a plugin itself, called ep_etherpad-lite, and it has it's own ep.json... +was that clear? \ No newline at end of file diff --git a/src/ep.json b/src/ep.json index bc955dbf3..59cbf3aa4 100644 --- a/src/ep.json +++ b/src/ep.json @@ -3,7 +3,6 @@ { "name": "static", "hooks": { "expressCreateServer": "ep_etherpad-lite/node/hooks/express/static:expressCreateServer" } }, { "name": "specialpages", "hooks": { "expressCreateServer": "ep_etherpad-lite/node/hooks/express/specialpages:expressCreateServer" } }, { "name": "padurlsanitize", "hooks": { "expressCreateServer": "ep_etherpad-lite/node/hooks/express/padurlsanitize:expressCreateServer" } }, - { "name": "minified", "hooks": { "expressCreateServer": "ep_etherpad-lite/node/hooks/express/minified:expressCreateServer" } }, { "name": "padreadonly", "hooks": { "expressCreateServer": "ep_etherpad-lite/node/hooks/express/padreadonly:expressCreateServer" } }, { "name": "webaccess", "hooks": { "expressConfigure": "ep_etherpad-lite/node/hooks/express/webaccess:expressConfigure" } }, { "name": "apicalls", "hooks": { "expressCreateServer": "ep_etherpad-lite/node/hooks/express/apicalls:expressCreateServer" } }, diff --git a/src/node/hooks/express/minified.js b/src/node/hooks/express/minified.js deleted file mode 100644 index f8a988d77..000000000 --- a/src/node/hooks/express/minified.js +++ /dev/null @@ -1,6 +0,0 @@ -var minify = require('../../utils/Minify'); - -exports.expressCreateServer = function (hook_name, args, cb) { - //serve minified files - args.app.get(/^\/minified\/(.*)/, minify.minifyJS); -} diff --git a/src/node/hooks/express/static.js b/src/node/hooks/express/static.js index 9481eb5a2..e8f9afbb8 100644 --- a/src/node/hooks/express/static.js +++ b/src/node/hooks/express/static.js @@ -8,34 +8,9 @@ var fs = require("fs"); var ERR = require("async-stacktrace"); exports.expressCreateServer = function (hook_name, args, cb) { - /* Handle static files for plugins: - paths like "/static/plugins/ep_myplugin/js/test.js" - are rewritten into ROOT_PATH_OF_MYPLUGIN/static/js/test.js, - commonly ETHERPAD_ROOT/node_modules/ep_myplugin/static/js/test.js - */ - args.app.get(/^\/minified\/plugins\/([^\/]+)\/static\/(.*)/, function(req, res, next) { - var plugin_name = req.params[0]; - var modulePath = req.url.split("?")[0].substr("/minified/plugins/".length); - var fullPath = require.resolve(modulePath); - - if (plugins.plugins[plugin_name] == undefined) { - return next(); - } - - fs.readFile(fullPath, "utf8", function(err, data){ - if(ERR(err)) return; - - res.send("require.define('" + modulePath + "', function (require, exports, module) {" + data + "})"); - }) - -//require.define("/plugins.js", function (require, exports, module) { - - //res.sendfile(fullPath); - }); - // Cache both minified and static. var assetCache = new CachingMiddleware; - args.app.all('/(minified|static)/*', assetCache.handle); + args.app.all('/(javascripts|static)/*', assetCache.handle); // Minify will serve static files compressed (minify enabled). It also has // file-specific hacks for ace/require-kernel/etc. @@ -44,8 +19,10 @@ exports.expressCreateServer = function (hook_name, args, cb) { // Setup middleware that will package JavaScript files served by minify for // CommonJS loader on the client-side. var jsServer = new (Yajsml.Server)({ - rootPath: 'minified/' + rootPath: 'javascripts/src/' , rootURI: 'http://localhost:' + settings.port + '/static/js/' + , libraryPath: 'javascripts/lib/' + , libraryURI: 'http://localhost:' + settings.port + '/static/plugins/' }); var StaticAssociator = Yajsml.associators.StaticAssociator; diff --git a/src/node/utils/Minify.js b/src/node/utils/Minify.js index a49195a7b..f569d4b92 100644 --- a/src/node/utils/Minify.js +++ b/src/node/utils/Minify.js @@ -27,6 +27,7 @@ var cleanCSS = require('clean-css'); var jsp = require("uglify-js").parser; var pro = require("uglify-js").uglify; var path = require('path'); +var plugins = require("ep_etherpad-lite/static/js/pluginfw/plugins"); var RequireKernel = require('require-kernel'); var server = require('../server'); @@ -35,11 +36,14 @@ var TAR_PATH = path.join(__dirname, 'tar.json'); var tar = JSON.parse(fs.readFileSync(TAR_PATH, 'utf8')); // Rewrite tar to include modules with no extensions and proper rooted paths. +var LIBRARY_PREFIX = 'ep_etherpad-lite/static/js'; exports.tar = {}; for (var key in tar) { - exports.tar['/' + key] = - tar[key].map(function (p) {return '/' + p}).concat( - tar[key].map(function (p) {return '/' + p.replace(/\.js$/, '')}) + exports.tar[LIBRARY_PREFIX + '/' + key] = + tar[key].map(function (p) {return LIBRARY_PREFIX + '/' + p}).concat( + tar[key].map(function (p) { + return LIBRARY_PREFIX + '/' + p.replace(/\.js$/, '') + }) ); } @@ -63,6 +67,22 @@ exports.minify = function(req, res, next) return; } + /* Handle static files for plugins: + paths like "plugins/ep_myplugin/static/js/test.js" + are rewritten into ROOT_PATH_OF_MYPLUGIN/static/js/test.js, + commonly ETHERPAD_ROOT/node_modules/ep_myplugin/static/js/test.js + */ + var match = filename.match(/^plugins\/([^\/]+)\/static\/(.*)/); + if (match) { + var pluginName = match[1]; + var resourcePath = match[2]; + var plugin = plugins.plugins[pluginName]; + if (plugin) { + var pluginPath = plugin.package.realPath; + filename = path.relative(ROOT_DIR, pluginPath + '/static/' + resourcePath); + } + } + // What content type should this be? // TODO: This should use a MIME module. var contentType; @@ -149,8 +169,10 @@ function getAceFile(callback) { var request = require('request'); var baseURI = 'http://localhost:' + settings.port + var resourceURI = baseURI + path.normalize(path.join('/static/', filename)); + resourceURI = resourceURI.replace(/\\/g, '/'); // Windows (safe generally?) - request(baseURI + path.normalize(path.join('/static/', filename)), function (error, response, body) { + request(resourceURI, function (error, response, body) { if (!error && response.statusCode == 200) { data += 'Ace2Editor.EMBEDED[' + JSON.stringify(filename) + '] = ' + JSON.stringify(body || '') + ';\n'; diff --git a/src/node/utils/caching_middleware.js b/src/node/utils/caching_middleware.js index f25059b88..114356f23 100644 --- a/src/node/utils/caching_middleware.js +++ b/src/node/utils/caching_middleware.js @@ -21,9 +21,10 @@ var path = require('path'); var server = require('../server'); var zlib = require('zlib'); var util = require('util'); +var settings = require('./Settings'); -var ROOT_DIR = path.normalize(__dirname + "/../"); -var CACHE_DIR = ROOT_DIR + '../var/'; +var CACHE_DIR = path.normalize(path.join(settings.root, 'var/')); +CACHE_DIR = path.existsSync(CACHE_DIR) ? CACHE_DIR : undefined; var responseCache = {}; @@ -37,7 +38,7 @@ function CachingMiddleware() { } CachingMiddleware.prototype = new function () { function handle(req, res, next) { - if (!(req.method == "GET" || req.method == "HEAD")) { + if (!(req.method == "GET" || req.method == "HEAD") || !CACHE_DIR) { return next(undefined, req, res); } @@ -54,7 +55,7 @@ CachingMiddleware.prototype = new function () { var modifiedSince = (req.headers['if-modified-since'] && new Date(req.headers['if-modified-since'])); var lastModifiedCache = !error && stats.mtime; - if (lastModifiedCache) { + if (lastModifiedCache && responseCache[cacheKey]) { req.headers['if-modified-since'] = lastModifiedCache.toUTCString(); } else { delete req.headers['if-modified-since']; @@ -83,7 +84,7 @@ CachingMiddleware.prototype = new function () { && new Date(res.getHeader('last-modified'))); res.writeHead = old_res.writeHead; - if (status == 200 || status == 404) { + if (status == 200) { // Update cache var buffer = ''; diff --git a/src/package.json b/src/package.json index 556b1c494..80378a0a3 100644 --- a/src/package.json +++ b/src/package.json @@ -12,7 +12,7 @@ "dependencies" : { "yajsml" : "1.1.2", "request" : "2.9.100", - "require-kernel" : "1.0.3", + "require-kernel" : "1.0.5", "socket.io" : "0.8.7", "ueberDB" : "0.1.7", "async" : "0.1.18", diff --git a/src/static/js/Changeset.js b/src/static/js/Changeset.js index 2eb89c53b..fd1900ba7 100644 --- a/src/static/js/Changeset.js +++ b/src/static/js/Changeset.js @@ -25,7 +25,7 @@ * limitations under the License. */ -var AttributePoolFactory = require("ep_etherpad-lite/static/js/AttributePoolFactory"); +var AttributePoolFactory = require("./AttributePoolFactory"); var _opt = null; diff --git a/src/static/js/ace.js b/src/static/js/ace.js index 4114b63cf..1306dba01 100644 --- a/src/static/js/ace.js +++ b/src/static/js/ace.js @@ -28,7 +28,7 @@ Ace2Editor.registry = { nextId: 1 }; -var hooks = require('ep_etherpad-lite/static/js/pluginfw/hooks'); +var hooks = require('./pluginfw/hooks'); function Ace2Editor() { @@ -156,7 +156,11 @@ function Ace2Editor() } function pushRequireScriptTo(buffer) { var KERNEL_SOURCE = '../static/js/require-kernel.js'; - var KERNEL_BOOT = 'require.setRootURI("../minified/");\nrequire.setLibraryURI("../minified/plugins/");\nrequire.setGlobalKeyPath("require");' + var KERNEL_BOOT = '\ +require.setRootURI("../javascripts/src");\n\ +require.setLibraryURI("../javascripts/lib");\n\ +require.setGlobalKeyPath("require");\n\ +'; if (Ace2Editor.EMBEDED && Ace2Editor.EMBEDED[KERNEL_SOURCE]) { buffer.push('\ - + diff --git a/src/static/timeslider.html b/src/static/timeslider.html index 377740f75..413fbe804 100644 --- a/src/static/timeslider.html +++ b/src/static/timeslider.html @@ -200,13 +200,13 @@ - +