From bb58c8443f2e89d7acb86432a7f5ed3be8ef1049 Mon Sep 17 00:00:00 2001 From: aaronjudd Date: Tue, 30 Aug 2016 18:35:47 -0700 Subject: [PATCH 1/6] allow multiple languages in Translations - allow array to pass multiple languages in addition to shopLanguage. --- .../publications/collections/translations.js | 44 ++++++++++++------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/server/publications/collections/translations.js b/server/publications/collections/translations.js index d9bba5feca7..fdff3399002 100644 --- a/server/publications/collections/translations.js +++ b/server/publications/collections/translations.js @@ -3,24 +3,34 @@ import { Reaction } from "/server/api"; /** * Translations publication - * @param {String} sessionLanguage - current sessionLanguage default to 'en' + * @param {String, Array} sessionLanguages - current sessionLanguage default to 'en' + * @returns { Object } returns Translations + * @todo like to see the langages validated more with a schema */ -Meteor.publish("Translations", function (sessionLanguage) { - check(sessionLanguage, Match.OneOf(null, String)); - // the language is prone to initialize empty at first, but - // we're reactive and will re-subscribe once we have the langauge - // on the client - if (sessionLanguage) { - const shopId = Reaction.getShopId(); - const shopLanguage = Shops.findOne(Reaction.getShopId()).language || "en"; - return Translations.find({ - $or: [{ - i18n: shopLanguage, - shopId: shopId - }, { - i18n: sessionLanguage, - shopId: shopId - }] +Meteor.publish("Translations", function (languages) { + check(languages, Match.OneOf(String, Array)); + const shopId = Reaction.getShopId(); + const shopLanguage = Shops.findOne(shopId).language; + const sessionLanguages = []; + const langTranQuery = []; + + // set shop default + sessionLanguages.push(shopLanguage); + // lets get all these langauges + if (typeof languages === "array") { + sessionLanguages.concat(languages); + } else { + sessionLanguages.push(languages); + } + // add in the shop filter + for (const sessionLanguage of sessionLanguages) { + langTranQuery.push({ + i18n: sessionLanguage, + shopId: shopId }); } + console.log(langTranQuery) + return Translations.find({ + $or: langTranQuery + }); }); From 19c93fde543b76f62fda66b8bf407290b0b7a6e0 Mon Sep 17 00:00:00 2001 From: aaronjudd Date: Tue, 30 Aug 2016 18:36:52 -0700 Subject: [PATCH 2/6] updated i18next and Translation load order --- client/modules/i18n/main.js | 119 +--------------- client/modules/i18n/startup.js | 139 +++++++++++++++++++ client/modules/i18n/templates/header/i18n.js | 10 +- server/publications/collections/members.js | 1 + 4 files changed, 156 insertions(+), 113 deletions(-) create mode 100644 client/modules/i18n/startup.js diff --git a/client/modules/i18n/main.js b/client/modules/i18n/main.js index 7a821d0b29f..4c743558c50 100644 --- a/client/modules/i18n/main.js +++ b/client/modules/i18n/main.js @@ -1,16 +1,9 @@ -import _ from "lodash"; import i18next from "i18next"; -import i18nextBrowserLanguageDetector from "i18next-browser-languagedetector"; -import i18nextLocalStorageCache from "i18next-localstorage-cache"; -import i18nextSprintfPostProcessor from "i18next-sprintf-postprocessor"; -import i18nextJquery from "jquery-i18next"; import { Meteor } from "meteor/meteor"; import { Tracker } from "meteor/tracker"; -import { Session } from "meteor/session"; import { SimpleSchema } from "meteor/aldeed:simple-schema"; import { Reaction } from "/client/api"; -import { Packages, Shops, Translations } from "/lib/collections"; -import * as Schemas from "/lib/collections/schemas"; +import { Packages, Translations } from "/lib/collections"; // // Reaction i18n Translations, RTL and Currency Exchange Support @@ -40,7 +33,7 @@ export function getBrowserLanguage() { * @param {String} name - name * @return {Object} return schema label object */ -function getLabelsFor(schema, name) { +export function getLabelsFor(schema, name) { const labels = {}; // loop through all the rendered form fields and generate i18n keys for (const fieldName of schema._schemaKeys) { @@ -68,7 +61,7 @@ function getLabelsFor(schema, name) { * @todo implement messaging hierarchy from simple-schema * @return {Object} returns i18n translated message for schema labels */ -function getMessagesFor() { +export function getMessagesFor() { const messages = {}; for (const message in SimpleSchema._globalMessages) { if ({}.hasOwnProperty.call(SimpleSchema._globalMessages, message)) { @@ -89,19 +82,14 @@ function getMessagesFor() { */ export const i18nextDep = new Tracker.Dependency(); export const localeDep = new Tracker.Dependency(); -const packageNamespaces = []; +export const packageNamespaces = []; Meteor.startup(() => { Tracker.autorun(function (c) { + // setting local and active packageNamespaces + // packageNamespaces are used to determine i18n namespace if (Reaction.Subscriptions.Shops.ready()) { - // TODO: i18nextBrowserLanguageDetector - // const defaultLanguage = lng.detect() || shop.language; - - // set default session language - Session.setDefault("language", getBrowserLanguage()); - - // every package gets a namespace, fetch them - // const packageNamespaces = []; + // every package gets a namespace, fetch them and export const packages = Packages.find({}, { fields: { name: 1 @@ -128,97 +116,4 @@ Meteor.startup(() => { }); }); -// use tracker autorun to detect language changes -Tracker.autorun(function () { - return Meteor.subscribe("Translations", Session.get("language"), () => { - // fetch reaction translations - const translations = Translations.find({}, { - fields: { - _id: 0 - } - }).fetch(); - - // map reduce translations into i18next formatting - const resources = translations.reduce(function (x, y) { - const ns = Object.keys(y.translation)[0]; - // first creating the structure, when add additional namespaces - if (x[y.i18n]) { - x[y.i18n][ns] = y.translation[ns]; - } else { - x[y.i18n] = y.translation; - } - return x; - }, {}); - - const shop = Shops.findOne(Reaction.getShopId()); - - // - // initialize i18next - // - i18next - .use(i18nextBrowserLanguageDetector) - .use(i18nextLocalStorageCache) - .use(i18nextSprintfPostProcessor) - .use(i18nextJquery) - .init({ - debug: false, - ns: packageNamespaces, // translation namespace for every package - defaultNS: "core", // reaction "core" is the default namespace - lng: Session.get("language"), // user session language - fallbackLng: shop ? shop.language : null, // Shop language - resources: resources - // saveMissing: true, - // missingKeyHandler: function (lng, ns, key, fallbackValue) { - // Meteor.call("i18n/addTranslation", lng, ns, key, fallbackValue); - // } - }, (err, t) => { - // someday this should work - // see: https://github.com/aldeed/meteor-simple-schema/issues/494 - for (const schema in _.omit(Schemas, "__esModule")) { - if ({}.hasOwnProperty.call(Schemas, schema)) { - const ss = Schemas[schema]; - ss.labels(getLabelsFor(ss, schema)); - ss.messages(getMessagesFor(ss, schema)); - } - } - - i18nextDep.changed(); - - // global first time init event finds and replaces - // data-i18n attributes in html/template source. - $elements = $("[data-i18n]").localize(); - - // apply language direction to html - if (t("languageDirection") === "rtl") { - return $("html").addClass("rtl"); - } - return $("html").removeClass("rtl"); - }); - }); -}); - -// -// init i18nextJquery -// -i18nextJquery.init(i18next, $, { - tName: "t", // --> appends $.t = i18next.t - i18nName: "i18n", // --> appends $.i18n = i18next - handleName: "localize", // --> appends $(selector).localize(opts); - selectorAttr: "data-i18n", // selector for translating elements - targetAttr: "data-i18n-target", // element attribute to grab target element to translate (if diffrent then itself) - parseDefaultValueFromContent: true // parses default values from content ele.val or ele.text -}); - -// global onRendered event finds and replaces -// data-i18n attributes in html/template source. -// uses methods from i18nextJquery -Template.onRendered(function () { - this.autorun((function () { - return function () { - i18nextDep.depend(); - $elements = $("[data-i18n]").localize(); - }; - })(this)); -}); - export default i18next; diff --git a/client/modules/i18n/startup.js b/client/modules/i18n/startup.js new file mode 100644 index 00000000000..fc707275193 --- /dev/null +++ b/client/modules/i18n/startup.js @@ -0,0 +1,139 @@ +import _ from "lodash"; +import i18nextBrowserLanguageDetector from "i18next-browser-languagedetector"; +import i18nextLocalStorageCache from "i18next-localstorage-cache"; +import i18nextSprintfPostProcessor from "i18next-sprintf-postprocessor"; +import i18nextJquery from "jquery-i18next"; +import { Meteor } from "meteor/meteor"; +import { Tracker } from "meteor/tracker"; +import { Session } from "meteor/session"; + +import { Reaction } from "/client/api"; +import { Shops, Translations } from "/lib/collections"; +import * as Schemas from "/lib/collections/schemas"; +import i18next, { packageNamespaces, getLabelsFor, getMessagesFor, i18nextDep } from "./main"; + +const options = { + // order and from where user language should be detected + order: ["querystring", "cookie", "localStorage", "navigator", "htmlTag"], + + // keys or params to lookup language from + lookupQuerystring: "lng", + lookupCookie: "i18next", + lookupLocalStorage: "i18nextLng", + + // cache user language on + caches: ["localStorage", "cookie"], + + // optional expire and domain for set cookie + // cookieMinutes: 10, + // cookieDomain: "myDomain", + + // optional htmlTag with lang attribute, the default is: + htmlTag: document.documentElement +}; + + +Meteor.startup(() => { + // use tracker autorun to detect language changes + Tracker.autorun(function () { + if (Reaction.Subscriptions.Shops.ready() && Meteor.user()) { + const shop = Shops.findOne(Reaction.getShopId()); + let language = shop.language; + if (Meteor.user() && Meteor.user().profile && Meteor.user().profile.lang) { + language = Meteor.user().profile.lang; + } + // + // subscribe to user + shop Translations + // + return Meteor.subscribe("Translations", language, () => { + console.log("sub trans", language) + // fetch reaction translations + const translations = Translations.find({}, { + fields: { + _id: 0 + } + }).fetch(); + + // map reduce translations into i18next formatting + const resources = translations.reduce(function (x, y) { + const ns = Object.keys(y.translation)[0]; + // first creating the structure, when add additional namespaces + if (x[y.i18n]) { + x[y.i18n][ns] = y.translation[ns]; + } else { + x[y.i18n] = y.translation; + } + return x; + }, {}); + + // + // initialize i18next + // + i18next + .use(i18nextBrowserLanguageDetector) + .use(i18nextLocalStorageCache) + .use(i18nextSprintfPostProcessor) + .use(i18nextJquery) + .init({ + detection: options, + debug: false, + ns: packageNamespaces, // translation namespace for every package + defaultNS: "core", // reaction "core" is the default namespace + lng: language, // user session language + fallbackLng: shop ? shop.language : null, // Shop language + resources: resources + // saveMissing: true, + // missingKeyHandler: function (lng, ns, key, fallbackValue) { + // Meteor.call("i18n/addTranslation", lng, ns, key, fallbackValue); + // } + }, (err, t) => { + // someday this should work + // see: https://github.com/aldeed/meteor-simple-schema/issues/494 + for (const schema in _.omit(Schemas, "__esModule")) { + if ({}.hasOwnProperty.call(Schemas, schema)) { + const ss = Schemas[schema]; + ss.labels(getLabelsFor(ss, schema)); + ss.messages(getMessagesFor(ss, schema)); + } + } + + i18nextDep.changed(); + + // global first time init event finds and replaces + // data-i18n attributes in html/template source. + $elements = $("[data-i18n]").localize(); + + // apply language direction to html + if (t("languageDirection") === "rtl") { + return $("html").addClass("rtl"); + } + return $("html").removeClass("rtl"); + }); + }); // return + } + }); + + // + // init i18nextJquery + // + i18nextJquery.init(i18next, $, { + tName: "t", // --> appends $.t = i18next.t + i18nName: "i18n", // --> appends $.i18n = i18next + handleName: "localize", // --> appends $(selector).localize(opts); + selectorAttr: "data-i18n", // selector for translating elements + targetAttr: "data-i18n-target", // element attribute to grab target element to translate (if diffrent then itself) + parseDefaultValueFromContent: true // parses default values from content ele.val or ele.text + }); + + // global onRendered event finds and replaces + // data-i18n attributes in html/template source. + // uses methods from i18nextJquery + Template.onRendered(function () { + this.autorun((function () { + return function () { + i18nextDep.depend(); + $elements = $("[data-i18n]").localize(); + }; + })(this)); + }); +}); diff --git a/client/modules/i18n/templates/header/i18n.js b/client/modules/i18n/templates/header/i18n.js index 26af4cca63a..e9f0f705df3 100644 --- a/client/modules/i18n/templates/header/i18n.js +++ b/client/modules/i18n/templates/header/i18n.js @@ -1,7 +1,7 @@ import { Reaction } from "/client/api"; import { Shops } from "/lib/collections"; import { Session } from "meteor/session"; - +import { i18nextDep } from "../../main"; /** * i18nChooser helpers */ @@ -23,11 +23,13 @@ Template.i18nChooser.helpers({ } } } + return languages; }, active() { if (Session.equals("language", this.i18n)) { return "active"; } + return ""; } }); @@ -38,6 +40,12 @@ Template.i18nChooser.helpers({ Template.i18nChooser.events({ "click .i18n-language"(event) { event.preventDefault(); + // + // this is a sanctioned use of Meteor.user.update + // + // console.log("this.i18n", this.i18n) + Meteor.users.update(Meteor.userId(), {$set: {"profile.lang": this.i18n}}); + return Session.set("language", this.i18n); } }); diff --git a/server/publications/collections/members.js b/server/publications/collections/members.js index acc4c79e86a..6808a4bf112 100644 --- a/server/publications/collections/members.js +++ b/server/publications/collections/members.js @@ -34,6 +34,7 @@ Meteor.publish("ShopMembers", function () { emails: 1, username: 1, roles: 1, + "profile.lang": 1, "services.google.name": 1, "services.google.email": 1, "services.google.picture": 1, From ce38dc81f00567ff5125824ca747b43e57fd8576 Mon Sep 17 00:00:00 2001 From: aaronjudd Date: Tue, 30 Aug 2016 19:36:41 -0700 Subject: [PATCH 3/6] remove session handling from i18n MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - remove “language” session - rely on Meteor.user updates (stored lang in profile) - update i18nchooser helpers to insert user profile lang into selected element class --- client/modules/i18n/startup.js | 5 ++-- .../modules/i18n/templates/header/i18n.html | 2 +- client/modules/i18n/templates/header/i18n.js | 25 +++++++++++-------- server/publications/collections/accounts.js | 1 + .../publications/collections/translations.js | 2 +- 5 files changed, 19 insertions(+), 16 deletions(-) diff --git a/client/modules/i18n/startup.js b/client/modules/i18n/startup.js index fc707275193..b9c885f7cba 100644 --- a/client/modules/i18n/startup.js +++ b/client/modules/i18n/startup.js @@ -5,8 +5,6 @@ import i18nextSprintfPostProcessor from "i18next-sprintf-postprocessor"; import i18nextJquery from "jquery-i18next"; import { Meteor } from "meteor/meteor"; import { Tracker } from "meteor/tracker"; -import { Session } from "meteor/session"; - import { Reaction } from "/client/api"; import { Shops, Translations } from "/lib/collections"; import * as Schemas from "/lib/collections/schemas"; @@ -35,6 +33,8 @@ const options = { Meteor.startup(() => { // use tracker autorun to detect language changes + // this only runs on initial page loaded + // and when user.profile.lang updates Tracker.autorun(function () { if (Reaction.Subscriptions.Shops.ready() && Meteor.user()) { const shop = Shops.findOne(Reaction.getShopId()); @@ -46,7 +46,6 @@ Meteor.startup(() => { // subscribe to user + shop Translations // return Meteor.subscribe("Translations", language, () => { - console.log("sub trans", language) // fetch reaction translations const translations = Translations.find({}, { fields: { diff --git a/client/modules/i18n/templates/header/i18n.html b/client/modules/i18n/templates/header/i18n.html index 10daac5c9d3..da9f628ace1 100644 --- a/client/modules/i18n/templates/header/i18n.html +++ b/client/modules/i18n/templates/header/i18n.html @@ -9,7 +9,7 @@
  • {{#each languages}} -
  • +
  • {{label}}
  • {{/each}} diff --git a/client/modules/i18n/templates/header/i18n.js b/client/modules/i18n/templates/header/i18n.js index e9f0f705df3..1835c0d3887 100644 --- a/client/modules/i18n/templates/header/i18n.js +++ b/client/modules/i18n/templates/header/i18n.js @@ -1,7 +1,5 @@ import { Reaction } from "/client/api"; import { Shops } from "/lib/collections"; -import { Session } from "meteor/session"; -import { i18nextDep } from "../../main"; /** * i18nChooser helpers */ @@ -15,6 +13,18 @@ Template.i18nChooser.helpers({ for (const language of shop.languages) { if (language.enabled === true) { language.translation = "languages." + language.label.toLowerCase(); + // appending a helper to let us know this + // language is currently selected + const profile = Meteor.user().profile; + if (profile && profile.lang) { + if (profile.lang === language.i18n) { + language.class = "active"; + } + } else if (shop.language === language.i18n) { + // we don't have a profile language + // use the shop default + language.class = "active"; + } languages.push(language); } } @@ -24,12 +34,6 @@ Template.i18nChooser.helpers({ } } return languages; - }, - active() { - if (Session.equals("language", this.i18n)) { - return "active"; - } - return ""; } }); @@ -42,10 +46,9 @@ Template.i18nChooser.events({ event.preventDefault(); // // this is a sanctioned use of Meteor.user.update + // and only possible because we allow it in the + // UserProfile and ShopMembers publications. // - // console.log("this.i18n", this.i18n) Meteor.users.update(Meteor.userId(), {$set: {"profile.lang": this.i18n}}); - - return Session.set("language", this.i18n); } }); diff --git a/server/publications/collections/accounts.js b/server/publications/collections/accounts.js index 4ff94545491..74ff8c1eac8 100644 --- a/server/publications/collections/accounts.js +++ b/server/publications/collections/accounts.js @@ -55,6 +55,7 @@ Meteor.publish("UserProfile", function (profileUserId) { // no need to normal user so see his password hash const fields = { "emails": 1, + "profile.lang": 1, "profile.firstName": 1, "profile.lastName": 1, "profile.familyName": 1, diff --git a/server/publications/collections/translations.js b/server/publications/collections/translations.js index fdff3399002..7aa4f573287 100644 --- a/server/publications/collections/translations.js +++ b/server/publications/collections/translations.js @@ -29,7 +29,7 @@ Meteor.publish("Translations", function (languages) { shopId: shopId }); } - console.log(langTranQuery) + return Translations.find({ $or: langTranQuery }); From d60e9bc489487cc5d3aa020c3aff43c58ba1613f Mon Sep 17 00:00:00 2001 From: aaronjudd Date: Tue, 30 Aug 2016 22:27:27 -0700 Subject: [PATCH 4/6] retain language on anonymous user registration --- client/modules/i18n/startup.js | 12 +++++++----- client/modules/i18n/templates/header/i18n.js | 2 +- server/startup/accounts.js | 17 +++++++++++++++-- 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/client/modules/i18n/startup.js b/client/modules/i18n/startup.js index b9c885f7cba..26f01511883 100644 --- a/client/modules/i18n/startup.js +++ b/client/modules/i18n/startup.js @@ -10,6 +10,13 @@ import { Shops, Translations } from "/lib/collections"; import * as Schemas from "/lib/collections/schemas"; import i18next, { packageNamespaces, getLabelsFor, getMessagesFor, i18nextDep } from "./main"; +// +// setup options for i18nextBrowserLanguageDetector +// note: this isn't fully operational yet +// language is set by user currently +// progress toward detecting language +// should focus around i18nextBrowserLanguageDetector +// const options = { // order and from where user language should be detected order: ["querystring", "cookie", "localStorage", "navigator", "htmlTag"], @@ -21,11 +28,6 @@ const options = { // cache user language on caches: ["localStorage", "cookie"], - - // optional expire and domain for set cookie - // cookieMinutes: 10, - // cookieDomain: "myDomain", - // optional htmlTag with lang attribute, the default is: htmlTag: document.documentElement }; diff --git a/client/modules/i18n/templates/header/i18n.js b/client/modules/i18n/templates/header/i18n.js index 1835c0d3887..3edb065bfbf 100644 --- a/client/modules/i18n/templates/header/i18n.js +++ b/client/modules/i18n/templates/header/i18n.js @@ -7,7 +7,7 @@ import { Shops } from "/lib/collections"; Template.i18nChooser.helpers({ languages() { const languages = []; - if (Reaction.Subscriptions.Shops.ready()) { + if (Reaction.Subscriptions.Shops.ready() && Meteor.user()) { const shop = Shops.findOne(); if (typeof shop === "object" && shop.languages) { for (const language of shop.languages) { diff --git a/server/startup/accounts.js b/server/startup/accounts.js index 2567e7592a4..6b56000853b 100644 --- a/server/startup/accounts.js +++ b/server/startup/accounts.js @@ -51,7 +51,6 @@ export default function () { if (!options.anonymous) { return {}; } - let loginHandler; const stampedToken = Accounts._generateStampedLoginToken(); const userId = Accounts.insertUserDoc({ services: { @@ -59,7 +58,7 @@ export default function () { }, token: stampedToken.token }); - loginHandler = { + const loginHandler = { type: "anonymous", userId: userId }; @@ -72,6 +71,9 @@ export default function () { * adds Accounts record for reaction user profiles * we clone the user into accounts, as the user collection is * only to be used for authentication. + * - defaultVisitorRole + * - defaultRoles + * can be overriden from Shops * * @see: http://docs.meteor.com/#/full/accounts_oncreateuser */ @@ -85,6 +87,17 @@ export default function () { profile: Object.assign({}, options && options.profile) }; if (!user.emails) user.emails = []; + if (!user.profile) user.profile = {}; + + // retain language when user has defined a language + // perhaps should be treated as additionals + // or in onLogin below, or in the anonymous method options + // so many choices... + const currentUser = Meteor.user(); + if (currentUser && currentUser.profile && currentUser.profile.lang && !user.profile.lang) { + user.profile.lang = currentUser.profile.lang; + } + // init default user roles // we won't create users unless we have a shop. if (shop) { From e37469cd864fe5f01b23c73a88228cd793516324 Mon Sep 17 00:00:00 2001 From: aaronjudd Date: Wed, 31 Aug 2016 15:31:39 -0700 Subject: [PATCH 5/6] check admin before setting lang MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit — don’t set lang on initial admin --- server/startup/accounts.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/server/startup/accounts.js b/server/startup/accounts.js index 6b56000853b..f350b80d0a6 100644 --- a/server/startup/accounts.js +++ b/server/startup/accounts.js @@ -87,15 +87,17 @@ export default function () { profile: Object.assign({}, options && options.profile) }; if (!user.emails) user.emails = []; - if (!user.profile) user.profile = {}; // retain language when user has defined a language // perhaps should be treated as additionals // or in onLogin below, or in the anonymous method options // so many choices... - const currentUser = Meteor.user(); - if (currentUser && currentUser.profile && currentUser.profile.lang && !user.profile.lang) { - user.profile.lang = currentUser.profile.lang; + if (!(Meteor.users.find().count() === 0)) { // dont set on inital admin + if (!user.profile) user.profile = {}; + const currentUser = Meteor.user(); + if (currentUser && currentUser.profile && currentUser.profile.lang && !user.profile.lang) { + user.profile.lang = currentUser.profile.lang; + } } // init default user roles From c9703fbff2831a07708728e21a2a6da0c9dcc0d6 Mon Sep 17 00:00:00 2001 From: aaronjudd Date: Wed, 31 Aug 2016 16:22:17 -0700 Subject: [PATCH 6/6] updated accounts.js - cleanup checks --- server/startup/accounts.js | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/server/startup/accounts.js b/server/startup/accounts.js index f350b80d0a6..7382dcfe530 100644 --- a/server/startup/accounts.js +++ b/server/startup/accounts.js @@ -87,22 +87,20 @@ export default function () { profile: Object.assign({}, options && options.profile) }; if (!user.emails) user.emails = []; - - // retain language when user has defined a language - // perhaps should be treated as additionals - // or in onLogin below, or in the anonymous method options - // so many choices... - if (!(Meteor.users.find().count() === 0)) { // dont set on inital admin - if (!user.profile) user.profile = {}; - const currentUser = Meteor.user(); - if (currentUser && currentUser.profile && currentUser.profile.lang && !user.profile.lang) { - user.profile.lang = currentUser.profile.lang; - } - } - // init default user roles // we won't create users unless we have a shop. if (shop) { + // retain language when user has defined a language + // perhaps should be treated as additionals + // or in onLogin below, or in the anonymous method options + if (!(Meteor.users.find().count() === 0)) { // dont set on inital admin + if (!user.profile) user.profile = {}; + const currentUser = Meteor.user(user); + if (currentUser && currentUser.profile && currentUser.profile.lang && !user.profile.lang) { + user.profile.lang = currentUser.profile.lang; + } + } + // if we don't have user.services we're an anonymous user if (!user.services) { roles[shopId] = shop.defaultVisitorRole || defaultVisitorRole; @@ -152,7 +150,6 @@ export default function () { // run onCreateUser hooks // (the user object must be returned by all callbacks) const userDoc = Hooks.Events.run("onCreateUser", user, options); - return userDoc; } }); @@ -195,6 +192,7 @@ export default function () { const cart = Collections.Cart.findOne({ userId: options.user._id }); + // for a rare use cases if (typeof cart !== "object") return false; // in current version currentSessionId will be available for anonymous