diff --git a/packages/accounts-base/accounts_server.js b/packages/accounts-base/accounts_server.js index 9c26b38daf..5fdbf1737c 100644 --- a/packages/accounts-base/accounts_server.js +++ b/packages/accounts-base/accounts_server.js @@ -312,13 +312,14 @@ Meteor.publish(null, function() { // Accounts.addAutopublishFields Notably, this isn't implemented with // multiple publishes since DDP only merges only across top-level // fields, not subfields (such as 'services.facebook.accessToken') -autopublishFields = { +var autopublishFields = { loggedInUser: ['profile', 'username', 'emails'], otherUsers: ['profile', 'username'] }; // Add to the list of fields or subfields to be automatically -// published if autopublish is on +// published if autopublish is on. Must be called from top-level +// code (ie, before Meteor.startup hooks run). // // @param opts {Object} with: // - forLoggedInUser {Array} Array of fields published to the logged-in user @@ -330,42 +331,45 @@ Accounts.addAutopublishFields = function(opts) { autopublishFields.otherUsers, opts.forOtherUsers); }; -Meteor.server.onAutopublish(function () { - // ['profile', 'username'] -> {profile: 1, username: 1} - var toFieldSelector = function(fields) { - return _.object(_.map(fields, function(field) { - return [field, 1]; - })); - }; +if (Package.autopublish) { + // Use Meteor.startup to give other packages a chance to call + // addAutopublishFields. + Meteor.startup(function () { + // ['profile', 'username'] -> {profile: 1, username: 1} + var toFieldSelector = function(fields) { + return _.object(_.map(fields, function(field) { + return [field, 1]; + })); + }; + + Meteor.server.publish(null, function () { + if (this.userId) { + return Meteor.users.find( + {_id: this.userId}, + {fields: toFieldSelector(autopublishFields.loggedInUser)}); + } else { + return null; + } + }, /*suppress autopublish warning*/{is_auto: true}); + + // XXX this publish is neither dedup-able nor is it optimized by our special + // treatment of queries on a specific _id. Therefore this will have O(n^2) + // run-time performance every time a user document is changed (eg someone + // logging in). If this is a problem, we can instead write a manual publish + // function which filters out fields based on 'this.userId'. + Meteor.server.publish(null, function () { + var selector; + if (this.userId) + selector = {_id: {$ne: this.userId}}; + else + selector = {}; - Meteor.server.publish(null, function () { - if (this.userId) { return Meteor.users.find( - {_id: this.userId}, - {fields: toFieldSelector(autopublishFields.loggedInUser)}); - } else { - return null; - } - }, /*suppress autopublish warning*/{is_auto: true}); - - // XXX this publish is neither dedup-able nor is it optimized by our - // special treatment of queries on a specific _id. Therefore this - // will have O(n^2) run-time performance every time a user document - // is changed (eg someone logging in). If this is a problem, we can - // instead write a manual publish function which filters out fields - // based on 'this.userId'. - Meteor.server.publish(null, function () { - var selector; - if (this.userId) - selector = {_id: {$ne: this.userId}}; - else - selector = {}; - - return Meteor.users.find( - selector, - {fields: toFieldSelector(autopublishFields.otherUsers)}); - }, /*suppress autopublish warning*/{is_auto: true}); -}); + selector, + {fields: toFieldSelector(autopublishFields.otherUsers)}); + }, /*suppress autopublish warning*/{is_auto: true}); + }); +} // Publish all login service configuration fields other than secret. Meteor.publish("meteor.loginServiceConfiguration", function () { diff --git a/packages/accounts-base/package.js b/packages/accounts-base/package.js index 5bb44013fd..ec8f15eb9b 100644 --- a/packages/accounts-base/package.js +++ b/packages/accounts-base/package.js @@ -21,6 +21,10 @@ Package.on_use(function (api) { // {{currentUser}}. If not, no biggie. api.use('handlebars', 'client', {weak: true}); + // Allow us to detect 'autopublish', and publish some Meteor.users fields if + // it's loaded. + api.use('autopublish', 'server', {weak: true}); + api.exportSymbol('Accounts'); api.add_files('accounts_common.js', ['client', 'server']); diff --git a/packages/accounts-facebook/facebook.js b/packages/accounts-facebook/facebook.js index 1fb297ca25..4517bf1d5a 100644 --- a/packages/accounts-facebook/facebook.js +++ b/packages/accounts-facebook/facebook.js @@ -12,7 +12,6 @@ if (Meteor.isClient) { Facebook.requestCredential(options, credentialRequestCompleteCallback); }; } else { - Accounts.addAutopublishFields({ // publish all fields including access token, which can legitimately // be used from the client (if transmitted over ssl or on diff --git a/packages/accounts-github/github.js b/packages/accounts-github/github.js index fb0cb7f1a2..bf71477695 100644 --- a/packages/accounts-github/github.js +++ b/packages/accounts-github/github.js @@ -12,7 +12,6 @@ if (Meteor.isClient) { Github.requestCredential(options, credentialRequestCompleteCallback); }; } else { - Accounts.addAutopublishFields({ // not sure whether the github api can be used from the browser, // thus not sure if we should be sending access tokens; but we do it diff --git a/packages/accounts-google/google.js b/packages/accounts-google/google.js index 0d9a77ab6c..08538f7ae3 100644 --- a/packages/accounts-google/google.js +++ b/packages/accounts-google/google.js @@ -12,7 +12,6 @@ if (Meteor.isClient) { Google.requestCredential(options, credentialRequestCompleteCallback); }; } else { - Accounts.addAutopublishFields({ forLoggedInUser: _.map( // publish access token since it can be used from the client (if diff --git a/packages/accounts-meetup/meetup.js b/packages/accounts-meetup/meetup.js index f4ed38e482..08e8ec0938 100644 --- a/packages/accounts-meetup/meetup.js +++ b/packages/accounts-meetup/meetup.js @@ -12,7 +12,6 @@ if (Meteor.isClient) { Meetup.requestCredential(options, credentialRequestCompleteCallback); }; } else { - Accounts.addAutopublishFields({ // publish all fields including access token, which can legitimately // be used from the client (if transmitted over ssl or on @@ -20,6 +19,4 @@ if (Meteor.isClient) { forLoggedInUser: ['services.meetup'], forOtherUsers: ['services.meetup.id'] }); - - } diff --git a/packages/accounts-twitter/twitter.js b/packages/accounts-twitter/twitter.js index 19c4a9e209..176d82a655 100644 --- a/packages/accounts-twitter/twitter.js +++ b/packages/accounts-twitter/twitter.js @@ -12,7 +12,6 @@ if (Meteor.isClient) { Twitter.requestCredential(options, credentialRequestCompleteCallback); }; } else { - var autopublishedFields = _.map( // don't send access token. https://dev.twitter.com/discussions/5025 Twitter.whitelistedFields.concat(['id', 'screenName']), diff --git a/packages/accounts-weibo/weibo.js b/packages/accounts-weibo/weibo.js index f1eaf95518..f55ec19bc7 100644 --- a/packages/accounts-weibo/weibo.js +++ b/packages/accounts-weibo/weibo.js @@ -12,12 +12,10 @@ if (Meteor.isClient) { Weibo.requestCredential(options, credentialRequestCompleteCallback); }; } else { - Accounts.addAutopublishFields({ // publish all fields including access token, which can legitimately // be used from the client (if transmitted over ssl or on localhost) forLoggedInUser: ['services.weibo'], forOtherUsers: ['services.weibo.screenName'] }); - } diff --git a/packages/autopublish/autopublish.js b/packages/autopublish/autopublish.js deleted file mode 100644 index e45f4dee64..0000000000 --- a/packages/autopublish/autopublish.js +++ /dev/null @@ -1 +0,0 @@ -Meteor.server.autopublish(); diff --git a/packages/autopublish/package.js b/packages/autopublish/package.js index 9e824aa799..960a18afd8 100644 --- a/packages/autopublish/package.js +++ b/packages/autopublish/package.js @@ -2,7 +2,5 @@ Package.describe({ summary: "Automatically publish the entire database to all clients" }); -Package.on_use(function (api) { - api.use('livedata', 'server'); - api.add_files("autopublish.js", "server"); -}); +// This package is empty; its presence is detected by livedata and +// accounts-base. diff --git a/packages/livedata/livedata_connection.js b/packages/livedata/livedata_connection.js index 8efecf5af8..839e9f383a 100644 --- a/packages/livedata/livedata_connection.js +++ b/packages/livedata/livedata_connection.js @@ -906,8 +906,8 @@ _.extend(Connection.prototype, { // Mark all named subscriptions which are ready (ie, we already called the // ready callback) as needing to be revived. - // XXX We should also block reconnect quiescence until autopublish is done - // re-publishing to avoid flicker! + // XXX We should also block reconnect quiescence until unnamed subscriptions + // (eg, autopublish) are done re-publishing to avoid flicker! self._subsBeingRevived = {}; _.each(self._subscriptions, function (sub, id) { if (sub.ready) diff --git a/packages/livedata/livedata_server.js b/packages/livedata/livedata_server.js index 6627159b44..bd9fe3d6b3 100644 --- a/packages/livedata/livedata_server.js +++ b/packages/livedata/livedata_server.js @@ -1024,9 +1024,6 @@ Server = function () { self.method_handlers = {}; - self.on_autopublish = []; // array of func if AP disabled, null if enabled - self.warned_about_autopublish = false; - self.sessions = {}; // map from id to session self.stream_server = new StreamServer; @@ -1176,7 +1173,7 @@ _.extend(Server.prototype, { return; } - if (!self.on_autopublish && !options.is_auto) { + if (Package.autopublish && !options.is_auto) { // They have autopublish on, yet they're trying to manually // picking stuff to publish. They probably should turn off // autopublish. (This check isn't perfect -- if you create a @@ -1307,25 +1304,6 @@ _.extend(Server.prototype, { if (exception) throw exception; return result; - }, - - // A much more elegant way to do this would be: let any autopublish - // provider (eg, mongo-livedata) declare a weak package dependency - // on the autopublish package, then have that package simply set a - // flag that eg the Collection constructor checks, and autopublishes - // if necessary. - autopublish: function () { - var self = this; - _.each(self.on_autopublish || [], function (f) { f(); }); - self.on_autopublish = null; - }, - - onAutopublish: function (f) { - var self = this; - if (self.on_autopublish) - self.on_autopublish.push(f); - else - f(); } }); diff --git a/packages/livedata/package.js b/packages/livedata/package.js index 14c0f3c7d3..a7cb9def76 100644 --- a/packages/livedata/package.js +++ b/packages/livedata/package.js @@ -19,6 +19,10 @@ Package.on_use(function (api) { // Detect whether or not the user wants us to audit argument checks. api.use(['audit-argument-checks'], 'server', {weak: true}); + // Allow us to detect 'autopublish', so we can print a warning if the user + // runs Meteor.publish while it's loaded. + api.use('autopublish', 'server', {weak: true}); + api.exportSymbol('DDP'); api.exportSymbol('DDPServer', 'server'); diff --git a/packages/mongo-livedata/collection.js b/packages/mongo-livedata/collection.js index fa98d62ad2..9288a3d710 100644 --- a/packages/mongo-livedata/collection.js +++ b/packages/mongo-livedata/collection.js @@ -174,12 +174,12 @@ Meteor.Collection = function (name, options) { self._defineMutationMethods(); // autopublish - if (!options._preventAutopublish && - self._connection && self._connection.onAutopublish) - self._connection.onAutopublish(function () { - var handler = function () { return self.find(); }; - self._connection.publish(null, handler, {is_auto: true}); - }); + if (Package.autopublish && !options._preventAutopublish && self._connection + && self._connection.publish) { + self._connection.publish(null, function () { + return self.find(); + }, {is_auto: true}); + } }; /// diff --git a/packages/mongo-livedata/package.js b/packages/mongo-livedata/package.js index 33e673dd4c..d674e56643 100644 --- a/packages/mongo-livedata/package.js +++ b/packages/mongo-livedata/package.js @@ -20,8 +20,12 @@ Package.on_use(function (api) { ['client', 'server']); api.use('check', ['client', 'server']); + // Allow us to detect 'insecure'. api.use('insecure', {weak: true}); + // Allow us to detect 'autopublish', and publish collections if it's loaded. + api.use('autopublish', 'server', {weak: true}); + api.exportSymbol('_MongoLivedataTest', 'server'); api.add_files('mongo_driver.js', 'server');