diff --git a/History.md b/History.md index 24f763d382..364ca06904 100644 --- a/History.md +++ b/History.md @@ -1,8 +1,15 @@ ## vNEXT +## v0.3.9 + +* Add `spiderable` package to allow web crawlers to index Meteor apps. + +* `meteor deploy` uses SSL to protect application deployment. + * Fix `stopImmediatePropagation()`. #205 + ## v0.3.8 * HTTPS support diff --git a/README.md b/README.md index 6d94e88b0f..db7258e60a 100644 --- a/README.md +++ b/README.md @@ -74,3 +74,4 @@ in several ways: * IRC: ```#meteor``` on ```irc.freenode.net``` * Ask a question: http://stackoverflow.com/questions/tagged/meteor * Email us: ```contact@meteor.com``` +* How to contribute to Meteor: https://github.com/meteor/meteor/wiki diff --git a/admin/debian/changelog b/admin/debian/changelog index 1eec8a34ea..e7af1136c1 100644 --- a/admin/debian/changelog +++ b/admin/debian/changelog @@ -1,4 +1,4 @@ -meteor (0.3.8-1) unstable; urgency=low +meteor (0.3.9-1) unstable; urgency=low * Automated debian build. diff --git a/admin/install-s3.sh b/admin/install-s3.sh index 46f122a00c..a98a5a6f93 100755 --- a/admin/install-s3.sh +++ b/admin/install-s3.sh @@ -5,7 +5,7 @@ ## example. URLBASE="https://d3sqy0vbqsdhku.cloudfront.net" -VERSION="0.3.8" +VERSION="0.3.9" PKGVERSION="${VERSION}-1" UNAME=`uname` diff --git a/admin/manifest.json b/admin/manifest.json index 07f0e7ce07..6909f075f4 100644 --- a/admin/manifest.json +++ b/admin/manifest.json @@ -1,6 +1,6 @@ { - "version": "0.3.8", - "deb_version": "0.3.8-1", - "rpm_version": "0.3.8-1", + "version": "0.3.9", + "deb_version": "0.3.9-1", + "rpm_version": "0.3.9-1", "urlbase": "https://d3sqy0vbqsdhku.cloudfront.net" } diff --git a/admin/meteor.spec b/admin/meteor.spec index 3d8fcc5660..b158cf9dfd 100644 --- a/admin/meteor.spec +++ b/admin/meteor.spec @@ -5,7 +5,7 @@ Summary: Meteor platform and JavaScript application server Vendor: Meteor Name: meteor -Version: 0.3.8 +Version: 0.3.9 Release: 1 License: MIT Group: Networking/WWW diff --git a/app/lib/updater.js b/app/lib/updater.js index 24762d830b..d4fda9083c 100644 --- a/app/lib/updater.js +++ b/app/lib/updater.js @@ -1,4 +1,4 @@ -exports.CURRENT_VERSION = "0.3.8"; +exports.CURRENT_VERSION = "0.3.9"; var fs = require("fs"); var http = require("http"); diff --git a/app/meteor/deploy.js b/app/meteor/deploy.js index 264a18a562..bcc8be6de1 100644 --- a/app/meteor/deploy.js +++ b/app/meteor/deploy.js @@ -23,7 +23,7 @@ var DEPLOY_HOSTNAME = 'deploy.meteor.com'; // interactively prompt for here. var meteor_rpc = function (rpc_name, method, site, query_params, callback) { - var url = "http://" + DEPLOY_HOSTNAME + '/' + rpc_name + '/' + site; + var url = "https://" + DEPLOY_HOSTNAME + '/' + rpc_name + '/' + site; if (!_.isEmpty(query_params)) url += '?' + qs.stringify(query_params); @@ -309,7 +309,7 @@ var read_password = function (callback) { // called exactly once. Calls callback with the entered password, or // undefined if no password is required. var with_password = function (site, callback) { - var check_url = "http://" + DEPLOY_HOSTNAME + "/has_password/" + site; + var check_url = "https://" + DEPLOY_HOSTNAME + "/has_password/" + site; request(check_url, function (error, response, body) { if (error || response.statusCode !== 200) { diff --git a/app/meteor/post-upgrade.js b/app/meteor/post-upgrade.js index dbb3a2d33b..d1755703ce 100644 --- a/app/meteor/post-upgrade.js +++ b/app/meteor/post-upgrade.js @@ -2,7 +2,7 @@ try { // XXX can't get this from updater.js because in 0.3.7 and before the // updater didn't have the right NODE_PATH set. At some point we can // remove this and just use updater.CURRENT_VERSION. - var VERSION = "0.3.8"; + var VERSION = "0.3.9"; var fs = require('fs'); var path = require('path'); diff --git a/docs/.meteor/packages b/docs/.meteor/packages index 78d056fca5..36eb4b5aea 100644 --- a/docs/.meteor/packages +++ b/docs/.meteor/packages @@ -9,3 +9,4 @@ showdown code-prettify jquery-waypoints less +spiderable diff --git a/docs/client/docs.html b/docs/client/docs.html index 9c2e4f06ff..7ac14a97f5 100644 --- a/docs/client/docs.html +++ b/docs/client/docs.html @@ -11,7 +11,7 @@
-

Meteor 0.3.8

+

Meteor 0.3.9

{{> introduction }} {{> concepts }} {{> api }} diff --git a/docs/client/docs.js b/docs/client/docs.js index 3018e1cd40..c79d83b199 100644 --- a/docs/client/docs.js +++ b/docs/client/docs.js @@ -1,4 +1,4 @@ -METEOR_VERSION = "0.3.8"; +METEOR_VERSION = "0.3.9"; Meteor.startup(function () { // XXX this is broken by the new multi-page layout. Also, it was @@ -194,6 +194,7 @@ var toc = [ "jquery", "less", "sass", + "spiderable", "stylus", "showdown", "underscore" diff --git a/docs/client/packages.html b/docs/client/packages.html index 65cbde7bd1..1c12157077 100644 --- a/docs/client/packages.html +++ b/docs/client/packages.html @@ -24,6 +24,7 @@ and removed with: {{> pkg_jquery}} {{> pkg_less}} {{> pkg_sass}} +{{> pkg_spiderable}} {{> pkg_stylus}} {{> pkg_showdown}} {{> pkg_underscore}} diff --git a/docs/client/packages/spiderable.html b/docs/client/packages/spiderable.html new file mode 100644 index 0000000000..2255f02978 --- /dev/null +++ b/docs/client/packages/spiderable.html @@ -0,0 +1,41 @@ + diff --git a/examples/todos/.meteor/packages b/examples/todos/.meteor/packages index 388ffe4ce1..c71f4c1a54 100644 --- a/examples/todos/.meteor/packages +++ b/examples/todos/.meteor/packages @@ -5,6 +5,7 @@ underscore backbone +spiderable accounts accounts-ui accounts-weibo diff --git a/examples/todos/client/todos.css b/examples/todos/client/todos.css index f311998666..405d35d494 100644 --- a/examples/todos/client/todos.css +++ b/examples/todos/client/todos.css @@ -138,6 +138,8 @@ h3 { #lists .list-name { cursor: pointer; + color: black; + text-decoration: none; } #createList { diff --git a/examples/todos/client/todos.html b/examples/todos/client/todos.html index 85910a1805..e408e7afb3 100644 --- a/examples/todos/client/todos.html +++ b/examples/todos/client/todos.html @@ -30,9 +30,9 @@
{{else}}
-
+ {{name}} -
+
{{/if}} diff --git a/examples/todos/client/todos.js b/examples/todos/client/todos.js index c18aa57271..307f1fcc69 100644 --- a/examples/todos/client/todos.js +++ b/examples/todos/client/todos.js @@ -87,6 +87,10 @@ Template.lists.events = { 'mousedown .list': function (evt) { // select list Router.setList(this._id); }, + 'click .list': function (evt) { + // prevent clicks on from refreshing the page. + evt.preventDefault(); + }, 'dblclick .list': function (evt) { // start editing list name Session.set('editing_listname', this._id); Meteor.flush(); // force DOM redraw, so we can focus the edit field diff --git a/packages/livedata/livedata_connection.js b/packages/livedata/livedata_connection.js index 9e08e96b04..42be89615e 100644 --- a/packages/livedata/livedata_connection.js +++ b/packages/livedata/livedata_connection.js @@ -760,7 +760,9 @@ _.extend(Meteor, { // "http://subdomain.meteor.com/sockjs" (deprecated), // "/sockjs" (deprecated) connect: function (url, _restartOnUpdate) { - return new Meteor._LivedataConnection(url, _restartOnUpdate); + var ret = new Meteor._LivedataConnection(url, _restartOnUpdate); + Meteor._LivedataConnection._allConnections.push(ret); // hack. see below. + return ret; }, autosubscribe: function (sub_func) { @@ -789,3 +791,14 @@ _.extend(Meteor, { } }); + +// Hack for `spiderable` package: a way to see if the page is done +// loading all the data it needs. +Meteor._LivedataConnection._allConnections = []; +Meteor._LivedataConnection._allSubscriptionsReady = function () { + return _.all(Meteor._LivedataConnection._allConnections, function (conn) { + for (var k in conn.sub_ready_callbacks) + return false; + return true; + }); +}; diff --git a/packages/spiderable/package.js b/packages/spiderable/package.js new file mode 100644 index 0000000000..6951f41fc0 --- /dev/null +++ b/packages/spiderable/package.js @@ -0,0 +1,10 @@ +Package.describe({ + summary: "Makes the application crawlable to web spiders." +}); + +Package.on_use(function (api) { + api.use(['templating'], 'client'); + + api.add_files('spiderable.html', 'client'); + api.add_files('spiderable.js', 'server'); +}); diff --git a/packages/spiderable/spiderable.html b/packages/spiderable/spiderable.html new file mode 100644 index 0000000000..7cbdf71b89 --- /dev/null +++ b/packages/spiderable/spiderable.html @@ -0,0 +1 @@ + diff --git a/packages/spiderable/spiderable.js b/packages/spiderable/spiderable.js new file mode 100644 index 0000000000..153baa4f0e --- /dev/null +++ b/packages/spiderable/spiderable.js @@ -0,0 +1,90 @@ +(function () { + var fs = __meteor_bootstrap__.require('fs'); + var spawn = __meteor_bootstrap__.require('child_process').spawn; + var querystring = __meteor_bootstrap__.require('querystring'); + var app = __meteor_bootstrap__.app; + + // how long to let phantomjs run before we kill it + var REQUEST_TIMEOUT = 15*1000; + + app.use(function (req, res, next) { + if (/\?.*_escaped_fragment_=/.test(req.url)) { + // get escaped fragment out of the url. + var idx = req.url.indexOf('?'); + var preQuery = req.url.substr(0, idx); + var queryStr = req.url.substr(idx + 1); + var parsed = querystring.parse(queryStr); + delete parsed['_escaped_fragment_']; + var newQuery = querystring.stringify(parsed); + var newPath = preQuery + (newQuery ? "?" + newQuery : ""); + var url = "http://" + req.headers.host + newPath; + + // run phantomjs + // + // Use '/dev/stdin' to avoid writing to a temporary file. Can't + // just omit the file, as PhantomJS takes that to mean 'use a + // REPL' and exits as soon as stdin closes. + var cp = spawn('phantomjs', ['--load-images=no', '/dev/stdin']); + + var data = ''; + cp.stdout.setEncoding('utf8'); + cp.stdout.on('data', function (chunk) { + data += chunk; + }); + + cp.on('exit', function (code) { + if (0 === code && //i.test(data)) { + res.writeHead(200, {'Content-Type': 'text/html; charset=UTF-8'}); + res.end(data); + } else { + // phantomjs failed. Don't send the error, instead send the + // normal page. + if (code === 127) + Meteor._debug("spiderable: phantomjs not installed. Download and install from http://phantomjs.org/"); + else + Meteor._debug("spiderable: phantomjs failed:", code, data); + + next(); + } + }); + + // don't crash w/ EPIPE if phantomjs isn't installed. + cp.stdin.on('error', function () {}); + + cp.stdin.write( + "var url = '" + url + "';" + +"var page = require('webpage').create();" + +"page.open(url);" + + +"setInterval(function() {" + +" var ready = page.evaluate(function () {" + +" if (typeof Meteor !== 'undefined' && Meteor.status().connected) {" + +" Meteor.flush();" + +" return Meteor._LivedataConnection._allSubscriptionsReady();" + +" }" + +" return false;" + +" });" + + +" if (ready) {" + +" var out = page.content;" + +" out = out.replace(/]+>(.|\\n|\\r)*?<\\/script\\s*>/ig, '');" + +" out = out.replace('', '');" + + +" console.log(out);" + +" phantom.exit();" + +" }" + +"}, 100);"); + cp.stdin.end(); + + // Just kill it if it takes too long. + setTimeout(function () { + if (cp && cp.pid) { + cp.kill(); + } + }, REQUEST_TIMEOUT); + + } else { + next(); + } + }); +})();