Accounts.sendLoginEmail = function (userId, address) {
  // XXX Also generate a link using which someone can delete this
  // account if they own said address but weren't those who created
  // this account.

  // Make sure the user exists, and address is one of their addresses.
  var user = Meteor.users.findOne(userId);
  if (!user)
    throw new Error("Can't find user");
  // pick the first unverified address if we weren't passed an address.
  if (!address) {
    var email = _.find(user.emails || [],
                       function (e) { return !e.verified; });
    address = (email || {}).address;
  }
  // make sure we have a valid address
  if (!address || !_.contains(_.pluck(user.emails || [], 'address'), address))
    throw new Error("No such email address for user.");


  var tokenRecord = {
    token: Random.secret(),
    address: address,
    when: new Date()};
  Meteor.users.update(
    {_id: userId},
    {$push: {'services.email.verificationTokens': tokenRecord}});

  // before passing to template, update user object with new token
  Meteor._ensure(user, 'services', 'email');
  if (!user.services.email.verificationTokens) {
    user.services.email.verificationTokens = [];
  }
  user.services.email.verificationTokens.push(tokenRecord);

  var loginUrl = Accounts.urls.verifyEmail(tokenRecord.token);

  var options = {
    to: email,
    from: Accounts.emailTemplates.loginNoPassword.from
      ? Accounts.emailTemplates.loginNoPassword.from(user)
      : Accounts.emailTemplates.from,
    subject: Accounts.emailTemplates.loginNoPassword.subject(user)
  };

  if (typeof Accounts.emailTemplates.loginNoPassword.text === 'function') {
    options.text =
      Accounts.emailTemplates.loginNoPassword.text(user, loginUrl);
  }

  if (typeof Accounts.emailTemplates.loginNoPassword.html === 'function')
    options.html =
      Accounts.emailTemplates.loginNoPassword.html(user, loginUrl);

  if (typeof Accounts.emailTemplates.headers === 'object') {
    options.headers = Accounts.emailTemplates.headers;
  }

  Email.send(options);
};
/**
 * @method sendVerificationEmail
 * @memberof Core
 * @summary Send an email with a link the user can use verify their email address.
 * @param {String} userId - The id of the user to send email to.
 * @param {String} [email] Optional. Address to send the email to.
 *                 This address must be in the user's emails list.
 *                 Defaults to the first unverified email in the list.
 * @return {Job} - returns a sendEmail Job instance
 */
export default async function sendVerificationEmail(userId, email) {
  // Make sure the user exists, and email is one of their addresses.
  const user = Meteor.users.findOne(userId);

  if (!user) {
    Logger.error("sendVerificationEmail - User not found");
    throw new ReactionError("not-found", "User not found");
  }

  let address = email;

  // pick the first unverified address if no address provided.
  if (!email) {
    const unverifiedEmail = _.find(user.emails || [], (e) => !e.verified) || {};

    ({ address } = unverifiedEmail);

    if (!address) {
      const msg = "No unverified email addresses found.";
      Logger.error(msg);
      throw new ReactionError("not-found", msg);
    }
  }

  // make sure we have a valid address
  if (!address || !user.emails || !(user.emails.map((mailInfo) => mailInfo.address).includes(address))) {
    const msg = "Email not found for user";
    Logger.error(msg);
    throw new ReactionError("not-found", msg);
  }

  const token = Random.secret();
  const when = new Date();
  const tokenObj = { token, address, when };

  Meteor.users.update({ _id: userId }, {
    $push: {
      "services.email.verificationTokens": tokenObj
    }
  });

  const shopName = Core.getShopName();
  const url = Accounts.urls.verifyEmail(token);
  const copyrightDate = new Date().getFullYear();

  const dataForEmail = {
    // Reaction Information
    contactEmail: "hello@reactioncommerce.com",
    homepage: Reaction.absoluteUrl(),
    emailLogo: `${Reaction.absoluteUrl()}resources/placeholder.gif`,
    copyrightDate,
    legalName: "Reaction Commerce",
    physicalAddress: {
      address: "2110 Main Street, Suite 207",
      city: "Santa Monica",
      region: "CA",
      postal: "90405"
    },
    shopName,
    socialLinks: {
      facebook: {
        link: "https://www.facebook.com/reactioncommerce"
      },
      github: {
        link: "https://github.com/reactioncommerce/reaction"
      },
      instagram: {
        link: "https://instagram.com/reactioncommerce"
      },
      twitter: {
        link: "https://www.twitter.com/getreaction"
      }
    },
    confirmationUrl: url,
    userEmailAddress: address
  };

  if (!Email.getMailUrl()) {
    Logger.warn(`

  ***************************************************
          IMPORTANT! EMAIL VERIFICATION LINK

           Email sending is not configured.

  Go to the following URL to verify email: ${address}

  ${url}
  ***************************************************

    `);
  }

  const tpl = "accounts/verifyEmail";
  const subject = "accounts/verifyEmail/subject";

  SSR.compileTemplate(tpl, Email.getTemplate(tpl));
  SSR.compileTemplate(subject, Email.getSubject(tpl));

  return Email.send({
    to: address,
    from: Core.getShopEmail(),
    subject: SSR.render(subject, dataForEmail),
    html: SSR.render(tpl, dataForEmail)
  });
}
示例#3
0
export async function sendResetPasswordEmail(userId, optionalEmail) {
  // Make sure the user exists, and email is one of their addresses.
  const user = Meteor.users.findOne(userId);

  if (!user) {
    Logger.error("sendResetPasswordEmail - User not found");
    throw new Meteor.Error("not-found", "User not found");
  }

  let email = optionalEmail;

  // pick the first email if we weren't passed an email.
  if (!optionalEmail && user.emails && user.emails[0]) {
    email = user.emails[0].address;
  }

  // make sure we have a valid email
  if (!email || !user.emails || !user.emails.map((mailInfo) => mailInfo.address).includes(email)) {
    Logger.error("sendResetPasswordEmail - Email not found");
    throw new Meteor.Error("not-found", "Email not found");
  }

  // Create token for password reset
  const token = Random.secret();
  const when = new Date();
  const tokenObj = { token, email, when };

  Meteor.users.update(userId, {
    $set: {
      "services.password.reset": tokenObj
    }
  });

  Meteor._ensure(user, "services", "password").reset = tokenObj;

  // Get shop data for email display
  const shop = Shops.findOne(Reaction.getShopId());
  const emailLogo = Reaction.Email.getShopLogo(shop);
  const copyrightDate = new Date().getFullYear();

  const dataForEmail = {
    // Shop Data
    shop,
    contactEmail: shop.emails[0].address,
    homepage: Meteor.absoluteUrl(),
    emailLogo,
    copyrightDate,
    legalName: _.get(shop, "addressBook[0].company"),
    physicalAddress: {
      address: `${_.get(shop, "addressBook[0].address1")} ${_.get(shop, "addressBook[0].address2")}`,
      city: _.get(shop, "addressBook[0].city"),
      region: _.get(shop, "addressBook[0].region"),
      postal: _.get(shop, "addressBook[0].postal")
    },
    shopName: shop.name,
    socialLinks: {
      display: true,
      facebook: {
        display: true,
        icon: `${Meteor.absoluteUrl()}resources/email-templates/facebook-icon.png`,
        link: "https://www.facebook.com"
      },
      googlePlus: {
        display: true,
        icon: `${Meteor.absoluteUrl()}resources/email-templates/google-plus-icon.png`,
        link: "https://plus.google.com"
      },
      twitter: {
        display: true,
        icon: `${Meteor.absoluteUrl()}resources/email-templates/twitter-icon.png`,
        link: "https://www.twitter.com"
      }
    },
    // Account Data
    passwordResetUrl: Accounts.urls.resetPassword(token),
    user
  };

  // Compile Email with SSR
  const tpl = "accounts/resetPassword";
  const subject = "accounts/resetPassword/subject";
  SSR.compileTemplate(tpl, Reaction.Email.getTemplate(tpl));
  SSR.compileTemplate(subject, Reaction.Email.getSubject(tpl));

  return Reaction.Email.send({
    to: email,
    from: Reaction.getShopEmail(),
    subject: SSR.render(subject, dataForEmail),
    html: SSR.render(tpl, dataForEmail)
  });
}
示例#4
0
export function inviteShopMember(shopId, email, name) {
  check(shopId, String);
  check(email, String);
  check(name, String);

  this.unblock();

  const shop = Shops.findOne(shopId);

  if (!shop) {
    const msg = `accounts/inviteShopMember - Shop ${shopId} not found`;
    Logger.error(msg);
    throw new Meteor.Error("shop-not-found", msg);
  }

  if (!Reaction.hasPermission("reaction-accounts", this.userId, shopId)) {
    Logger.error(`User ${this.userId} does not have reaction-accounts permissions`);
    throw new Meteor.Error("access-denied", "Access denied");
  }

  const currentUser = Meteor.users.findOne(this.userId);

  let currentUserName;

  if (currentUser) {
    if (currentUser.profile) {
      currentUserName = currentUser.profile.name || currentUser.username;
    } else {
      currentUserName = currentUser.username;
    }
  } else {
    currentUserName = "Admin";
  }

  const user = Meteor.users.findOne({
    "emails.address": email
  });

  if (!user) {
    const userId = MeteorAccounts.createUser({
      email: email,
      name: name,
      profile: {
        invited: true
      }
    });

    const newUser = Meteor.users.findOne(userId);

    if (!newUser) {
      throw new Error("Can't find user");
    }

    const token = Random.id();

    Meteor.users.update(userId, {
      $set: {
        "services.password.reset": { token, email, when: new Date() },
        "name": name
      }
    });

    // Get shop logo, if available. If not, use default logo from file-system
    let emailLogo;
    if (Array.isArray(shop.brandAssets)) {
      const brandAsset = _.find(shop.brandAssets, (asset) => asset.type === "navbarBrandImage");
      const mediaId = Media.findOne(brandAsset.mediaId);
      emailLogo = path.join(Meteor.absoluteUrl(), mediaId.url());
    } else {
      emailLogo = Meteor.absoluteUrl() + "resources/email-templates/shop-logo.png";
    }

    const dataForEmail = {
      // Shop Data
      shop: shop,
      contactEmail: shop.emails[0].address,
      homepage: Meteor.absoluteUrl(),
      emailLogo: emailLogo,
      copyrightDate: moment().format("YYYY"),
      legalName: shop.addressBook[0].company,
      physicalAddress: {
        address: shop.addressBook[0].address1 + " " + shop.addressBook[0].address2,
        city: shop.addressBook[0].city,
        region: shop.addressBook[0].region,
        postal: shop.addressBook[0].postal
      },
      shopName: shop.name,
      socialLinks: {
        display: true,
        facebook: {
          display: true,
          icon: Meteor.absoluteUrl() + "resources/email-templates/facebook-icon.png",
          link: "https://www.facebook.com"
        },
        googlePlus: {
          display: true,
          icon: Meteor.absoluteUrl() + "resources/email-templates/google-plus-icon.png",
          link: "https://plus.google.com"
        },
        twitter: {
          display: true,
          icon: Meteor.absoluteUrl() + "resources/email-templates/twitter-icon.png",
          link: "https://www.twitter.com"
        }
      },
      // Account Data
      user: Meteor.user(),
      currentUserName,
      invitedUserName: name,
      url: MeteorAccounts.urls.enrollAccount(token)
    };

    // Compile Email with SSR
    const tpl = "accounts/inviteShopMember";
    const subject = "accounts/inviteShopMember/subject";
    SSR.compileTemplate(tpl, Reaction.Email.getTemplate(tpl));
    SSR.compileTemplate(subject, Reaction.Email.getSubject(tpl));

    Reaction.Email.send({
      to: email,
      from: `${shop.name} <${shop.emails[0].address}>`,
      subject: SSR.render(subject, dataForEmail),
      html: SSR.render(tpl, dataForEmail)
    });
  } else {
    Reaction.Email.send({
      to: email,
      from: `${shop.name} <${shop.emails[0].address}>`,
      subject: SSR.render(subject, dataForEmail),
      html: SSR.render(tpl, dataForEmail)
    });
  }
  return true;
}
示例#5
0
文件: password.js 项目: SunFed/store
export function sendResetPasswordEmail(userId, optionalEmail) {
  // Make sure the user exists, and email is one of their addresses.
  const user = Meteor.users.findOne(userId);

  if (!user) {
    Logger.error("sendResetPasswordEmail - User not found");
    throw new Meteor.Error("user-not-found", "User not found");
  }

  let email = optionalEmail;

  // pick the first email if we weren't passed an email.
  if (!optionalEmail && user.emails && user.emails[0]) {
    email = user.emails[0].address;
  }

  // make sure we have a valid email
  if (!email || !user.emails || !user.emails.map((mailInfo) => mailInfo.address).includes(email)) {
    Logger.error("sendResetPasswordEmail - Email not found");
    throw new Meteor.Error("email-not-found", "Email not found");
  }

  // Create token for password reset
  const token = Random.secret();
  const when = new Date();
  const tokenObj = { token, email, when };

  Meteor.users.update(userId, {
    $set: {
      "services.password.reset": tokenObj
    }
  });

  Meteor._ensure(user, "services", "password").reset = tokenObj;

  // Get shop data for email display
  const shop = Shops.findOne(Reaction.getShopId());

  // Get shop logo, if available. If not, use default logo from file-system
  let emailLogo;
  if (Array.isArray(shop.brandAssets)) {
    const brandAsset = _.find(shop.brandAssets, (asset) => asset.type === "navbarBrandImage");
    const mediaId = Media.findOne(brandAsset.mediaId);
    emailLogo = path.join(Meteor.absoluteUrl(), mediaId.url());
  } else {
    emailLogo = Meteor.absoluteUrl() + "resources/email-templates/shop-logo.png";
  }

  const dataForEmail = {
    // Shop Data
    shop: shop,
    contactEmail: shop.emails[0].address,
    homepage: Meteor.absoluteUrl(),
    emailLogo: emailLogo,
    copyrightDate: moment().format("YYYY"),
    legalName: shop.addressBook[0].company,
    physicalAddress: {
      address: shop.addressBook[0].address1 + " " + shop.addressBook[0].address2,
      city: shop.addressBook[0].city,
      region: shop.addressBook[0].region,
      postal: shop.addressBook[0].postal
    },
    shopName: shop.name,
    socialLinks: {
      display: true,
      facebook: {
        display: true,
        icon: Meteor.absoluteUrl() + "resources/email-templates/facebook-icon.png",
        link: "https://www.facebook.com"
      },
      googlePlus: {
        display: true,
        icon: Meteor.absoluteUrl() + "resources/email-templates/google-plus-icon.png",
        link: "https://plus.google.com"
      },
      twitter: {
        display: true,
        icon: Meteor.absoluteUrl() + "resources/email-templates/twitter-icon.png",
        link: "https://www.twitter.com"
      }
    },
    // Account Data
    passwordResetUrl: Accounts.urls.resetPassword(token),
    user: user
  };

  // Compile Email with SSR
  const tpl = "accounts/resetPassword";
  const subject = "accounts/resetPassword/subject";
  SSR.compileTemplate(tpl, Reaction.Email.getTemplate(tpl));
  SSR.compileTemplate(subject, Reaction.Email.getSubject(tpl));

  return Reaction.Email.send({
    to: email,
    from: Reaction.getShopEmail(),
    subject: SSR.render(subject, dataForEmail),
    html: SSR.render(tpl, dataForEmail)
  });
}
示例#6
0
 function getEmailUrl(userToken) {
   if (userToken) {
     return MeteorAccounts.urls.enrollAccount(userToken);
   }
   return Reaction.absoluteUrl();
 }