<commit_before>import Notifications from '../collections/notifications/collection.js';
import Messages from '../collections/messages/collection.js';
import Conversations from '../collections/conversations/collection.js';
import Sequences from '../collections/sequences/collection.js';
import Users from 'meteor/vulcan:users';
import { Posts, Categories, Comments } from 'meteor/example-forum';
import { addCallback, newMutation, editMutation, Utils, runCallbacksAsync } from 'meteor/vulcan:core';
import { performSubscriptionAction } from '../subscriptions/mutations.js';
import h2p from 'html2plaintext';
import ReactDOMServer from 'react-dom/server';
import { Components } from 'meteor/vulcan:core';
import React from 'react';
import { anchorate } from 'anchorate';

function updateConversationActivity (message) {
  // Update latest Activity timestamp on conversation when new message is added
  const user = Users.findOne(message.userId);
  const conversation = Conversations.findOne(message.conversationId);
  editMutation({
    collection: Conversations,
    documentId: conversation._id,
    set: {latestActivity: message.createdAt},
    currentUser: user,
    validate: false,
  });
}

addCallback("messages.new.async", updateConversationActivity);

const createNotifications = (userIds, notificationType, documentType, documentId) => {
  console.log("Notifications are being created for users: ", userIds, notificationType);
  userIds.forEach(userId => {

    let user = Users.findOne({ _id:userId });

    let notificationData = {
      userId: userId,
      documentId: documentId,
      documentType: documentType,
      message: notificationMessage(notificationType, documentType, documentId),
      type: notificationType,
      link: getLink(documentType, documentId),
    }

    newMutation({
      action: 'notifications.new',
      collection: Notifications,
      document: notificationData,
      currentUser: user,
      validate: false
    });
  });
}

const getLink = (documentType, documentId) => {
  let document = getDocument(documentType, documentId);

  switch(documentType) {
    case "post":
      return Posts.getPageUrl(document);
    case "comment":
      return Comments.getPageUrl(document);
    case "user":
      return Users.getProfileUrl(document, false);
    case "message":
      return Messages.getLink(document);
    default:
      console.error("Invalid notification type");
  }
}

const notificationMessage = (notificationType, documentType, documentId) => {
  let document = getDocument(documentType, documentId);

  switch(notificationType) {
    case "newPost":
      return Posts.getAuthorName(document) + ' has created a new post: ' + document.title;
    case "newPendingPost":
      return Posts.getAuthorName(document) + ' has a new post pending approval ' + document.title;
    case "postApproved":
      return 'Your post "' + document.title + '" has been approved';
    case "newComment":
      return Comments.getAuthorName(document) + ' left a new comment on "' + Posts.findOne(document.postId).title + '"';
    case "newReply":
      return Comments.getAuthorName(document) + ' replied to a comment on "' + Posts.findOne(document.postId).title + '"';
    case "newUser":
      return document.displayName + ' just signed up!';
    case "newMessage":
      let conversation = Conversations.findOne(document.conversationId);
      return Users.findOne(document.userId).displayName + ' sent you a new message' + (conversation.title ? (' in the conversation ' + conversation.title) : "") + '!';
    default:
      console.error("Invalid notification type");
  }
}

const getDocument = (documentType, documentId) => {
  switch(documentType) {
    case "post":
      return Posts.findOne(documentId);
    case "comment":
      return Comments.findOne(documentId);
    case "user":
      return Users.findOne(documentId);
    case "message":
      return Messages.findOne(documentId);
    default:
      console.error("Invalid documentType type");
  }
}

/**
 * @summary Add default subscribers to the new post.
 */
function PostsNewSubscriptions (post) {
  // Subscribe the post's author to comment notifications for the post
  // (if they have the proper setting turned on)
  const postAuthor = Users.findOne(post.userId);
  if (Users.getSetting(postAuthor, "auto_subscribe_to_my_posts", false)) {

    performSubscriptionAction('subscribe', Posts, post._id, postAuthor);
  }
}
addCallback("posts.new.async", PostsNewSubscriptions);

/**
 * @summary Add default subscribers to the new comment.
 */
function CommentsNewSubscriptions (comment) {
  // Subscribe the comment's author to reply notifications for the comment
  // (if they have the proper setting turned on)
  const commentAuthor = Users.findOne(comment.userId);
  if (Users.getSetting(commentAuthor, "auto_subscribe_to_my_comments", false)) {

    performSubscriptionAction('subscribe', Comments, comment._id, commentAuthor);
  }
}
addCallback("comments.new.async", CommentsNewSubscriptions);

/**
 * @summary Add notification callback when a post is approved
 */
const PostsApprovedNotification = (post) => {
  createNotifications([post.userId], 'postApproved', 'post', post._id);
}
addCallback("posts.approve.async", PostsApprovedNotification);

/**
 * @summary Add new post notification callback on post submit
 */
function PostsNewNotifications (post) {
  if (post.status === Posts.config.STATUS_PENDING) {
    // if post is pending, only notify admins
    let adminIds = _.pluck(Users.adminUsers({fields: {_id:1}}), '_id');

    // remove this post's author
    adminIds = _.without(adminIds, post.userId);

    createNotifications(adminIds, 'newPendingPost', 'post', post._id);
  } else {
    // add users who get notifications for all new posts
    let usersToNotify = _.pluck(Users.find({'notifications_posts': true}, {fields: {_id:1}}).fetch(), '_id');

    // add users who are subscribed to this post's author
    const postAuthor = Users.findOne(post.userId);
    if (!!postAuthor.subscribers) {
      usersToNotify = _.union(usersToNotify, postAuthor.subscribers);
    }

    // add users who are subscribed to this post's categories
    if (!!post.categories) {
      post.categories.forEach(cid => {
        let c = Categories.findOne(cid);
        if (!!c.subscribers) {
          usersToNotify = _.union(usersToNotify, c.subscribers);
        }
      });
    }

    // remove this post's author
    usersToNotify = _.without(usersToNotify, post.userId);

    createNotifications(usersToNotify, 'newPost', 'post', post._id);
  }
}
addCallback("posts.new.async", PostsNewNotifications);

// add new comment notification callback on comment submit
function CommentsNewNotifications(comment) {
  // note: dummy content has disableNotifications set to true
  if(Meteor.isServer && !comment.disableNotifications) {

    const post = Posts.findOne(comment.postId);

    // keep track of whom we've notified (so that we don't notify the same user twice for one comment,
    // if e.g. they're both the author of the post and the author of a comment being replied to)
    let notifiedUsers = [];

    // 1. Notify users who are subscribed to the parent comment
    if (!!comment.parentCommentId) {
      const parentComment = Comments.findOne(comment.parentCommentId);

      if (!!parentComment.subscribers && !!parentComment.subscribers.length) {
        // remove userIds of users that have already been notified
        // and of comment author (they could be replying in a thread they're subscribed to)
        let parentCommentSubscribersToNotify = _.difference(parentComment.subscribers, notifiedUsers, [comment.userId]);
        createNotifications(parentCommentSubscribersToNotify, 'newReply', 'comment', comment._id);
        notifiedUsers = notifiedUsers.concat(parentCommentSubscribersToNotify);
      }
    }

    // 2. Notify users who are subscribed to the post (which may or may not include the post's author)
    if (!!post.subscribers && !!post.subscribers.length) {
      // remove userIds of users that have already been notified
      // and of comment author (they could be replying in a thread they're subscribed to)
      let postSubscribersToNotify = _.difference(post.subscribers, notifiedUsers, [comment.userId]);
      createNotifications(postSubscribersToNotify, 'newComment', 'comment', comment._id);
    }
  }
}
addCallback("comments.new.async", CommentsNewNotifications);

function messageNewNotification(message) {
  if(Meteor.isServer) {
    const conversation = Conversations.findOne(message.conversationId);
    //Make sure to not notify the author of the message
    const notifees = conversation.participantIds.filter((id) => (id != message.userId));

    createNotifications(notifees, 'newMessage', 'message', message._id);
  }
}
addCallback("messages.new.async", messageNewNotification);

function postsNewHTMLBodyAndPlaintextBody(post) {
  if (post.content) {
    const html = ReactDOMServer.renderToStaticMarkup(<Components.ContentRenderer state={post.content} />);
    const plaintextBody = h2p(html);
    const excerpt =  plaintextBody.slice(0,140);
    Posts.update(post._id, {$set: {htmlBody: html, body: plaintextBody, excerpt: excerpt}});
  } else if (post.htmlBody) {
    const html = post.htmlBody;
    const plaintextBody = h2p(html);
    const excerpt = plaintextBody.slice(0,140);
    Posts.update(post._id, {$set: {body: plaintextBody, excerpt: excerpt}});
  }
}

addCallback("posts.new.async", postsNewHTMLBodyAndPlaintextBody);
addCallback("posts.edit.async", postsNewHTMLBodyAndPlaintextBody);

function commentsNewHTMLBodyAndPlaintextBody(comment) {
  if (comment.content) {
    const html = ReactDOMServer.renderToStaticMarkup(<Components.ContentRenderer state={comment.content} />);
    const plaintextBody = h2p(html);
    Comments.update(comment._id, {$set: {htmlBody: html, body: plaintextBody}});
  } else if (comment.htmlBody){
    const html = comment.htmlBody;
    const plaintextBody = h2p(html);
    Comments.update(comment._id, {$set: {body: plaintextBody}});
  }
}

addCallback("comments.new.async", commentsNewHTMLBodyAndPlaintextBody);
addCallback("comments.edit.async", commentsNewHTMLBodyAndPlaintextBody);

function sequencesNewPlaintextDescription(sequence) {
  if (sequence.description) {
    const html = ReactDOMServer.renderToStaticMarkup(<Components.ContentRenderer state={sequence.description} />);
    const plaintextBody = h2p(html);
    Sequences.update(sequence._id, {$set: {plaintextDescription: plaintextBody}});
  }
}

addCallback("sequences.new.async", sequencesNewPlaintextDescription);
addCallback("sequences.edit.async", sequencesNewPlaintextDescription);

function reactRouterAnchorTags(unusedItem) {
  anchorate();
  return unusedItem;
}

addCallback("router.onUpdate", reactRouterAnchorTags);

function userEditBanUserCallbacksAsync(user, oldUser) {
  if (user.banned && !oldUser.banned) {
    runCallbacksAsync('users.ban.async', user);
  }
  return user;
}
addCallback("users.edit.async", userEditBanUserCallbacksAsync);

function userEditVoteBannedCallbacksAsync(user, oldUser) {
  if (user.voteBanned && !oldUser.voteBanned) {
    runCallbacksAsync('users.voteBanned.async', user);
  }
  return user;
}
addCallback("users.edit.async", userEditVoteBannedCallbacksAsync);

function userEditNullifyVotesCallbacksAsync(user, oldUser) {
  console.log("userEditNullifyVotesCallback", user, oldUser);
  if (user.nullifyVotes && !oldUser.nullifyVotes) {
    runCallbacksAsync('users.nullifyVotes.async', user);
  }
  return user;
}
addCallback("users.edit.async", userEditNullifyVotesCallbacksAsync);

const reverseVote = (vote, collection, user, multiplier) => {
  const item = collection.findOne({_id: vote.itemId});
  if (item && item.baseScore) {
    collection.update({_id: vote.itemId}, {$set: {baseScore: (item.baseScore || 0) - (multiplier * vote.power)}})
    if (item.userId !== user._id) {
      Users.update({_id: item.userId}, {$inc: {karma: - (multiplier * vote.power)}})
    }
  } else {
    console.log("No item found corresponding to vote: ", vote);
  }
}

const nullifyVotesForUserAndCollection = (user, collection) => {
  const collectionName = Utils.capitalize(collection._name);

  if (user[`upvoted${collectionName}`] && user[`upvoted${collectionName}`].length){
    let voteArray = [];
    user[`upvoted${collectionName}`].forEach((vote) => {
      const newVote = { ...vote, nullified: true }
      voteArray.push(newVote)
      reverseVote(vote, collection, user, 1)
    })
    Users.update({_id: user._id}, {$set: {[`upvoted${collectionName}`]: voteArray}})
    console.log("Nullified n upvotes for user X: ", voteArray.length, user.displayName)
  }

  if (user[`downvoted${collectionName}`] && user[`downvoted${collectionName}`].length){
    let voteArray = [];
    user[`downvoted${collectionName}`].forEach((vote) => {
      const newVote = { ...vote, nullified: true }
      voteArray.push(newVote)
      reverseVote(vote, collection, user, -1)
    })
    Users.update({_id: user._id}, {$set: {[`downvoted${collectionName}`]: voteArray}})
    console.log("Nullified n downvotes for user X: ", voteArray.length, user.displayName)
  }
}

const undoNullifyVotesForUserAndCollection = (user, collection) => {

  const collectionName = Utils.capitalize(collection._name);

  if (user[`upvoted${collectionName}`] && user[`upvoted${collectionName}`].length){
    let voteArray = [];
    user[`upvoted${collectionName}`].forEach((vote) => {
      const newVote = { ...vote, nullified: false }
      voteArray.push(newVote)
      reverseVote(vote, collection, user, -1)
    })
    Users.update({_id: user._id}, {$set: {[`upvoted${collectionName}`]: voteArray}})
    console.log("Denullified n upvotes for user X: ", voteArray.length, user.displayName)
  }

  if (user[`downvoted${collectionName}`] && user[`downvoted${collectionName}`].length){
    let voteArray = [];
    user[`downvoted${collectionName}`].forEach((vote) => {
      const newVote = { ...vote, nullified: false }
      voteArray.push(newVote)
      reverseVote(vote, collection, user, 1)
    })
    Users.update({_id: user._id}, {$set: {[`downvoted${collectionName}`]: voteArray}})
    console.log("Denullified n downvotes for user X: ", voteArray.length, user.displayName)
  }
}

function nullifyCommentVotes(user) {
  nullifyVotesForUserAndCollection(user, Comments);
  return user;
}

addCallback("users.nullifyVotes.async", nullifyCommentVotes)

function nullifyPostVotes(user) {
  nullifyVotesForUserAndCollection(user, Posts);
  return user;
}

addCallback("users.nullifyVotes.async", nullifyPostVotes)

function userEditUndoNullifyVotesCallbacksAsync(user, oldUser) {
  if (!user.nullifyVotes && oldUser.nullifyVotes) {
    runCallbacksAsync('users.undoNullifyVotes.async', user);
  }
  return user;
}
addCallback("users.edit.async", userEditUndoNullifyVotesCallbacksAsync);

function undoNullifyCommentVotes(user) {
  undoNullifyVotesForUserAndCollection(user, Comments);
  return user;
}

addCallback("users.undoNullifyVotes.async", undoNullifyCommentVotes)

function undoNullifyPostVotes(user) {
  undoNullifyVotesForUserAndCollection(user, Posts);
  return user;
}

addCallback("users.undoNullifyVotes.async", undoNullifyPostVotes)
<commit_msg>Removed unnecessary log statement<commit_after>import Notifications from '../collections/notifications/collection.js';
import Messages from '../collections/messages/collection.js';
import Conversations from '../collections/conversations/collection.js';
import Sequences from '../collections/sequences/collection.js';
import Users from 'meteor/vulcan:users';
import { Posts, Categories, Comments } from 'meteor/example-forum';
import { addCallback, newMutation, editMutation, Utils, runCallbacksAsync } from 'meteor/vulcan:core';
import { performSubscriptionAction } from '../subscriptions/mutations.js';
import h2p from 'html2plaintext';
import ReactDOMServer from 'react-dom/server';
import { Components } from 'meteor/vulcan:core';
import React from 'react';
import { anchorate } from 'anchorate';

function updateConversationActivity (message) {
  // Update latest Activity timestamp on conversation when new message is added
  const user = Users.findOne(message.userId);
  const conversation = Conversations.findOne(message.conversationId);
  editMutation({
    collection: Conversations,
    documentId: conversation._id,
    set: {latestActivity: message.createdAt},
    currentUser: user,
    validate: false,
  });
}

addCallback("messages.new.async", updateConversationActivity);

const createNotifications = (userIds, notificationType, documentType, documentId) => {
  console.log("Notifications are being created for users: ", userIds, notificationType);
  userIds.forEach(userId => {

    let user = Users.findOne({ _id:userId });

    let notificationData = {
      userId: userId,
      documentId: documentId,
      documentType: documentType,
      message: notificationMessage(notificationType, documentType, documentId),
      type: notificationType,
      link: getLink(documentType, documentId),
    }

    newMutation({
      action: 'notifications.new',
      collection: Notifications,
      document: notificationData,
      currentUser: user,
      validate: false
    });
  });
}

const getLink = (documentType, documentId) => {
  let document = getDocument(documentType, documentId);

  switch(documentType) {
    case "post":
      return Posts.getPageUrl(document);
    case "comment":
      return Comments.getPageUrl(document);
    case "user":
      return Users.getProfileUrl(document, false);
    case "message":
      return Messages.getLink(document);
    default:
      console.error("Invalid notification type");
  }
}

const notificationMessage = (notificationType, documentType, documentId) => {
  let document = getDocument(documentType, documentId);

  switch(notificationType) {
    case "newPost":
      return Posts.getAuthorName(document) + ' has created a new post: ' + document.title;
    case "newPendingPost":
      return Posts.getAuthorName(document) + ' has a new post pending approval ' + document.title;
    case "postApproved":
      return 'Your post "' + document.title + '" has been approved';
    case "newComment":
      return Comments.getAuthorName(document) + ' left a new comment on "' + Posts.findOne(document.postId).title + '"';
    case "newReply":
      return Comments.getAuthorName(document) + ' replied to a comment on "' + Posts.findOne(document.postId).title + '"';
    case "newUser":
      return document.displayName + ' just signed up!';
    case "newMessage":
      let conversation = Conversations.findOne(document.conversationId);
      return Users.findOne(document.userId).displayName + ' sent you a new message' + (conversation.title ? (' in the conversation ' + conversation.title) : "") + '!';
    default:
      console.error("Invalid notification type");
  }
}

const getDocument = (documentType, documentId) => {
  switch(documentType) {
    case "post":
      return Posts.findOne(documentId);
    case "comment":
      return Comments.findOne(documentId);
    case "user":
      return Users.findOne(documentId);
    case "message":
      return Messages.findOne(documentId);
    default:
      console.error("Invalid documentType type");
  }
}

/**
 * @summary Add default subscribers to the new post.
 */
function PostsNewSubscriptions (post) {
  // Subscribe the post's author to comment notifications for the post
  // (if they have the proper setting turned on)
  const postAuthor = Users.findOne(post.userId);
  if (Users.getSetting(postAuthor, "auto_subscribe_to_my_posts", false)) {

    performSubscriptionAction('subscribe', Posts, post._id, postAuthor);
  }
}
addCallback("posts.new.async", PostsNewSubscriptions);

/**
 * @summary Add default subscribers to the new comment.
 */
function CommentsNewSubscriptions (comment) {
  // Subscribe the comment's author to reply notifications for the comment
  // (if they have the proper setting turned on)
  const commentAuthor = Users.findOne(comment.userId);
  if (Users.getSetting(commentAuthor, "auto_subscribe_to_my_comments", false)) {

    performSubscriptionAction('subscribe', Comments, comment._id, commentAuthor);
  }
}
addCallback("comments.new.async", CommentsNewSubscriptions);

/**
 * @summary Add notification callback when a post is approved
 */
const PostsApprovedNotification = (post) => {
  createNotifications([post.userId], 'postApproved', 'post', post._id);
}
addCallback("posts.approve.async", PostsApprovedNotification);

/**
 * @summary Add new post notification callback on post submit
 */
function PostsNewNotifications (post) {
  if (post.status === Posts.config.STATUS_PENDING) {
    // if post is pending, only notify admins
    let adminIds = _.pluck(Users.adminUsers({fields: {_id:1}}), '_id');

    // remove this post's author
    adminIds = _.without(adminIds, post.userId);

    createNotifications(adminIds, 'newPendingPost', 'post', post._id);
  } else {
    // add users who get notifications for all new posts
    let usersToNotify = _.pluck(Users.find({'notifications_posts': true}, {fields: {_id:1}}).fetch(), '_id');

    // add users who are subscribed to this post's author
    const postAuthor = Users.findOne(post.userId);
    if (!!postAuthor.subscribers) {
      usersToNotify = _.union(usersToNotify, postAuthor.subscribers);
    }

    // add users who are subscribed to this post's categories
    if (!!post.categories) {
      post.categories.forEach(cid => {
        let c = Categories.findOne(cid);
        if (!!c.subscribers) {
          usersToNotify = _.union(usersToNotify, c.subscribers);
        }
      });
    }

    // remove this post's author
    usersToNotify = _.without(usersToNotify, post.userId);

    createNotifications(usersToNotify, 'newPost', 'post', post._id);
  }
}
addCallback("posts.new.async", PostsNewNotifications);

// add new comment notification callback on comment submit
function CommentsNewNotifications(comment) {
  // note: dummy content has disableNotifications set to true
  if(Meteor.isServer && !comment.disableNotifications) {

    const post = Posts.findOne(comment.postId);

    // keep track of whom we've notified (so that we don't notify the same user twice for one comment,
    // if e.g. they're both the author of the post and the author of a comment being replied to)
    let notifiedUsers = [];

    // 1. Notify users who are subscribed to the parent comment
    if (!!comment.parentCommentId) {
      const parentComment = Comments.findOne(comment.parentCommentId);

      if (!!parentComment.subscribers && !!parentComment.subscribers.length) {
        // remove userIds of users that have already been notified
        // and of comment author (they could be replying in a thread they're subscribed to)
        let parentCommentSubscribersToNotify = _.difference(parentComment.subscribers, notifiedUsers, [comment.userId]);
        createNotifications(parentCommentSubscribersToNotify, 'newReply', 'comment', comment._id);
        notifiedUsers = notifiedUsers.concat(parentCommentSubscribersToNotify);
      }
    }

    // 2. Notify users who are subscribed to the post (which may or may not include the post's author)
    if (!!post.subscribers && !!post.subscribers.length) {
      // remove userIds of users that have already been notified
      // and of comment author (they could be replying in a thread they're subscribed to)
      let postSubscribersToNotify = _.difference(post.subscribers, notifiedUsers, [comment.userId]);
      createNotifications(postSubscribersToNotify, 'newComment', 'comment', comment._id);
    }
  }
}
addCallback("comments.new.async", CommentsNewNotifications);

function messageNewNotification(message) {
  if(Meteor.isServer) {
    const conversation = Conversations.findOne(message.conversationId);
    //Make sure to not notify the author of the message
    const notifees = conversation.participantIds.filter((id) => (id != message.userId));

    createNotifications(notifees, 'newMessage', 'message', message._id);
  }
}
addCallback("messages.new.async", messageNewNotification);

function postsNewHTMLBodyAndPlaintextBody(post) {
  if (post.content) {
    const html = ReactDOMServer.renderToStaticMarkup(<Components.ContentRenderer state={post.content} />);
    const plaintextBody = h2p(html);
    const excerpt =  plaintextBody.slice(0,140);
    Posts.update(post._id, {$set: {htmlBody: html, body: plaintextBody, excerpt: excerpt}});
  } else if (post.htmlBody) {
    const html = post.htmlBody;
    const plaintextBody = h2p(html);
    const excerpt = plaintextBody.slice(0,140);
    Posts.update(post._id, {$set: {body: plaintextBody, excerpt: excerpt}});
  }
}

addCallback("posts.new.async", postsNewHTMLBodyAndPlaintextBody);
addCallback("posts.edit.async", postsNewHTMLBodyAndPlaintextBody);

function commentsNewHTMLBodyAndPlaintextBody(comment) {
  if (comment.content) {
    const html = ReactDOMServer.renderToStaticMarkup(<Components.ContentRenderer state={comment.content} />);
    const plaintextBody = h2p(html);
    Comments.update(comment._id, {$set: {htmlBody: html, body: plaintextBody}});
  } else if (comment.htmlBody){
    const html = comment.htmlBody;
    const plaintextBody = h2p(html);
    Comments.update(comment._id, {$set: {body: plaintextBody}});
  }
}

addCallback("comments.new.async", commentsNewHTMLBodyAndPlaintextBody);
addCallback("comments.edit.async", commentsNewHTMLBodyAndPlaintextBody);

function sequencesNewPlaintextDescription(sequence) {
  if (sequence.description) {
    const html = ReactDOMServer.renderToStaticMarkup(<Components.ContentRenderer state={sequence.description} />);
    const plaintextBody = h2p(html);
    Sequences.update(sequence._id, {$set: {plaintextDescription: plaintextBody}});
  }
}

addCallback("sequences.new.async", sequencesNewPlaintextDescription);
addCallback("sequences.edit.async", sequencesNewPlaintextDescription);

function reactRouterAnchorTags(unusedItem) {
  anchorate();
  return unusedItem;
}

addCallback("router.onUpdate", reactRouterAnchorTags);

function userEditBanUserCallbacksAsync(user, oldUser) {
  if (user.banned && !oldUser.banned) {
    runCallbacksAsync('users.ban.async', user);
  }
  return user;
}
addCallback("users.edit.async", userEditBanUserCallbacksAsync);

function userEditVoteBannedCallbacksAsync(user, oldUser) {
  if (user.voteBanned && !oldUser.voteBanned) {
    runCallbacksAsync('users.voteBanned.async', user);
  }
  return user;
}
addCallback("users.edit.async", userEditVoteBannedCallbacksAsync);

function userEditNullifyVotesCallbacksAsync(user, oldUser) {
  if (user.nullifyVotes && !oldUser.nullifyVotes) {
    runCallbacksAsync('users.nullifyVotes.async', user);
  }
  return user;
}
addCallback("users.edit.async", userEditNullifyVotesCallbacksAsync);

const reverseVote = (vote, collection, user, multiplier) => {
  const item = collection.findOne({_id: vote.itemId});
  if (item && item.baseScore) {
    collection.update({_id: vote.itemId}, {$set: {baseScore: (item.baseScore || 0) - (multiplier * vote.power)}})
    if (item.userId !== user._id) {
      Users.update({_id: item.userId}, {$inc: {karma: - (multiplier * vote.power)}})
    }
  } else {
    console.log("No item found corresponding to vote: ", vote);
  }
}

const nullifyVotesForUserAndCollection = (user, collection) => {
  const collectionName = Utils.capitalize(collection._name);

  if (user[`upvoted${collectionName}`] && user[`upvoted${collectionName}`].length){
    let voteArray = [];
    user[`upvoted${collectionName}`].forEach((vote) => {
      const newVote = { ...vote, nullified: true }
      voteArray.push(newVote)
      reverseVote(vote, collection, user, 1)
    })
    Users.update({_id: user._id}, {$set: {[`upvoted${collectionName}`]: voteArray}})
    console.log("Nullified n upvotes for user X: ", voteArray.length, user.displayName)
  }

  if (user[`downvoted${collectionName}`] && user[`downvoted${collectionName}`].length){
    let voteArray = [];
    user[`downvoted${collectionName}`].forEach((vote) => {
      const newVote = { ...vote, nullified: true }
      voteArray.push(newVote)
      reverseVote(vote, collection, user, -1)
    })
    Users.update({_id: user._id}, {$set: {[`downvoted${collectionName}`]: voteArray}})
    console.log("Nullified n downvotes for user X: ", voteArray.length, user.displayName)
  }
}

const undoNullifyVotesForUserAndCollection = (user, collection) => {

  const collectionName = Utils.capitalize(collection._name);

  if (user[`upvoted${collectionName}`] && user[`upvoted${collectionName}`].length){
    let voteArray = [];
    user[`upvoted${collectionName}`].forEach((vote) => {
      const newVote = { ...vote, nullified: false }
      voteArray.push(newVote)
      reverseVote(vote, collection, user, -1)
    })
    Users.update({_id: user._id}, {$set: {[`upvoted${collectionName}`]: voteArray}})
    console.log("Denullified n upvotes for user X: ", voteArray.length, user.displayName)
  }

  if (user[`downvoted${collectionName}`] && user[`downvoted${collectionName}`].length){
    let voteArray = [];
    user[`downvoted${collectionName}`].forEach((vote) => {
      const newVote = { ...vote, nullified: false }
      voteArray.push(newVote)
      reverseVote(vote, collection, user, 1)
    })
    Users.update({_id: user._id}, {$set: {[`downvoted${collectionName}`]: voteArray}})
    console.log("Denullified n downvotes for user X: ", voteArray.length, user.displayName)
  }
}

function nullifyCommentVotes(user) {
  nullifyVotesForUserAndCollection(user, Comments);
  return user;
}

addCallback("users.nullifyVotes.async", nullifyCommentVotes)

function nullifyPostVotes(user) {
  nullifyVotesForUserAndCollection(user, Posts);
  return user;
}

addCallback("users.nullifyVotes.async", nullifyPostVotes)

function userEditUndoNullifyVotesCallbacksAsync(user, oldUser) {
  if (!user.nullifyVotes && oldUser.nullifyVotes) {
    runCallbacksAsync('users.undoNullifyVotes.async', user);
  }
  return user;
}
addCallback("users.edit.async", userEditUndoNullifyVotesCallbacksAsync);

function undoNullifyCommentVotes(user) {
  undoNullifyVotesForUserAndCollection(user, Comments);
  return user;
}

addCallback("users.undoNullifyVotes.async", undoNullifyCommentVotes)

function undoNullifyPostVotes(user) {
  undoNullifyVotesForUserAndCollection(user, Posts);
  return user;
}

addCallback("users.undoNullifyVotes.async", undoNullifyPostVotes)
<|endoftext|>


<commit_before>import { Plugin } from 'munar-core'

const debug = require('debug')('munar:user-log')

export default class UserLog extends Plugin {
  static description = 'Keeps track of users who visit the channel.'

  constructor (bot, options) {
    super(bot, options)

    this.onUserJoin = this.onUserJoin.bind(this)
    this.onUserUpdate = this.onUserUpdate.bind(this)
  }

  async enable () {
    this.bot.on('user:join', this.onUserJoin)
    this.bot.on('user:update', this.onUserUpdate)

    const allUsers = this.bot.adapters.reduce((users, adapter) => {
      return [...users, ...adapter.getUsers()]
    }, [])

    try {
      await Promise.all(
        allUsers.map((user) =>
          this.onUserJoin(user.source, user)
        )
      )
    } catch (error) {
      console.error('Could not register all joined users')
      console.error(error.stack)
    }
  }

  disable () {
    this.bot.removeListener('user:join', this.onUserJoin)
    this.bot.removeListener('user:update', this.onUserUpdate)
  }

  async onUserJoin (adapter, user) {
    debug('join', `${user.username} (${user.id})`)
    const User = this.model('User')
    try {
      const userModel = await User.from(user)
      if (!userModel && adapter) {
        await User.create({
          ...user.compoundId(),
          username: user.username
        })
      }
    } catch (e) {
      console.error(e.stack || e)
    }
  }

  async onUserUpdate (adapter, user, update) {
    if (!user) return
    debug('update', update)
  }
}
<commit_msg>user-log: Update user models when names changed. (#137)<commit_after>import { Plugin } from 'munar-core'

const debug = require('debug')('munar:user-log')

export default class UserLog extends Plugin {
  static description = 'Keeps track of users who visit the channel.'

  constructor (bot, options) {
    super(bot, options)

    this.onUserJoin = this.onUserJoin.bind(this)
    this.onUserUpdate = this.onUserUpdate.bind(this)
  }

  async enable () {
    this.bot.on('user:join', this.onUserJoin)
    this.bot.on('user:update', this.onUserUpdate)

    const allUsers = this.bot.adapters.reduce((users, adapter) => {
      return [...users, ...adapter.getUsers()]
    }, [])

    try {
      await Promise.all(
        allUsers.map((user) =>
          this.onUserJoin(user.source, user)
        )
      )
    } catch (error) {
      console.error('Could not register all joined users')
      console.error(error.stack)
    }
  }

  disable () {
    this.bot.removeListener('user:join', this.onUserJoin)
    this.bot.removeListener('user:update', this.onUserUpdate)
  }

  async onUserJoin (adapter, user) {
    debug('join', `${user.username} (${user.id})`)
    const User = this.model('User')
    if (!adapter) {
      return
    }
    try {
      const userModel = await User.from(user)
      if (!userModel) {
        await User.create({
          ...user.compoundId(),
          username: user.username
        })
      } else if (userModel.username !== user.username) {
        await userModel.update({
          username: user.username
        })
      }
    } catch (e) {
      console.error(e.stack || e)
    }
  }

  async onUserUpdate (adapter, user, update) {
    if (!user) return

    const User = this.model('User')
    debug('update', update)

    if (update.username) {
      const userModel = await User.from(user)
      if (userModel) {
        await userModel.update({
          username: user.username
        })
      }
    }
  }
}
<|endoftext|>


<commit_before>// @flow

import invariant from 'assert'
import generate from '@babel/generator'
import * as t from '@babel/types'
import { promisify } from 'util'
import { transform } from '@babel/core'
import { createFileTransformer, getChunk, getChunkHash, getFileImportHash } from 'pundle-api'

import pluginTransformNodeEnvInline from 'babel-plugin-transform-node-env-inline'

import pluginRemoveDeadNodes from './plugin-remove-dead-nodes'
import manifest from '../package.json'
import { getName } from './helpers'

const INJECTIONS = new Map([['timers', ['setImmediate', 'clearImmediate']], ['buffer', ['Buffer']], ['process', 'process']])
const INJECTIONS_NAMES = new Map()
INJECTIONS.forEach(function(names, sourceModule) {
  ;[].concat(names).forEach(function(name) {
    INJECTIONS_NAMES.set(name, sourceModule)
  })
})

const transformAsync = promisify(transform)

// TODO: have a config?
export default function({ transformCore }: { transformCore: boolean }) {
  return createFileTransformer({
    name: 'pundle-transformer-js',
    version: manifest.version,
    priority: 1000,
    async callback({ file, resolve, addImport, addChunk }) {
      if (file.format !== 'js') return null

      const promises = []
      const injectionNames = new Set()
      const { ast } = await transformAsync(typeof file.contents === 'string' ? file.contents : file.contents.toString(), {
        ast: true,
        code: false,
        babelrc: false,
        configFile: false,
        sourceMaps: false,
        filename: file.filePath,
        highlightCode: false,
        plugins: [
          pluginTransformNodeEnvInline,
          pluginRemoveDeadNodes,
          {
            visitor: {
              ImportDeclaration({ node }) {
                const { source } = node
                if (!t.isStringLiteral(source)) return
                promises.push(
                  resolve(source.value, source.loc).then(resolved => {
                    source.value = getFileImportHash(resolved)
                    addImport(resolved)
                  }),
                )
              },
              CallExpression(path) {
                const { node } = path
                const { callee } = node
                const [arg] = node.arguments

                if (!t.isStringLiteral(arg)) return

                if (t.isImport(callee)) {
                  promises.push(
                    resolve(arg.value, arg.loc).then(resolved => {
                      const chunk = getChunk(resolved.format, null, resolved.filePath)
                      node.callee = t.memberExpression(t.identifier('require'), t.identifier('chunk'))
                      arg.value = getChunkHash(chunk)
                      addChunk(chunk)
                    }),
                  )
                  return
                }

                if (!['require', 'require.resolve'].includes(getName(callee)) || path.scope.hasBinding('require')) {
                  return
                }

                // Handling require + require.resolve
                promises.push(
                  resolve(arg.value, arg.loc).then(resolved => {
                    arg.value = getFileImportHash(resolved)
                    addImport(resolved)
                  }),
                )
              },
              ...(transformCore
                ? {
                    Identifier(path) {
                      const { node } = path
                      if (
                        INJECTIONS_NAMES.has(node.name) &&
                        !injectionNames.has(node.name) &&
                        path.isReferencedIdentifier() &&
                        !path.scope.hasBinding(node.name)
                      ) {
                        injectionNames.add(node.name)
                      }
                    },
                  }
                : {}),
            },
          },
        ],
        sourceType: 'module',
        parserOpts: {
          plugins: ['dynamicImport'],
        },
      })

      const injectionImports = new Map()
      injectionNames.forEach(item => {
        const sourceModule = INJECTIONS_NAMES.get(item)
        invariant(sourceModule, 'sourceModule for Injection was not found?')
        promises.push(
          resolve(sourceModule).then(resolved => {
            injectionImports.set(sourceModule, getFileImportHash(resolved))
            addImport(resolved)
          }),
        )
      })

      await Promise.all(promises)

      if (injectionImports.size) {
        const wrapper = t.callExpression(t.functionExpression(null, [], t.blockStatement([])), [])
        injectionImports.forEach((resolved, importName) => {
          wrapper.arguments.push(t.callExpression(t.identifier('require'), [t.stringLiteral(resolved)]))
          let names = INJECTIONS.get(importName)
          if (typeof names === 'string') {
            wrapper.callee.params.push(t.identifier(names))
          } else if (Array.isArray(names)) {
            names = names.filter(item => injectionNames.has(item))
            const refName = `__$sb$pundle${importName}`
            wrapper.callee.params.push(t.identifier(refName))
            ast.program.body = [
              t.variableDeclaration(
                'var',
                names.map(name =>
                  t.variableDeclarator(t.identifier(name), t.memberExpression(t.identifier(refName), t.identifier(name))),
                ),
              ),
            ].concat(ast.program.body)
          }
        })
        wrapper.callee.body.body = ast.program.body
        wrapper.callee.body.directives = ast.program.directives
        ast.program.body = [t.expressionStatement(wrapper)]
        ast.program.directives = []
      }

      const generated = generate(ast, {
        sourceMaps: true,
        sourceFileName: file.filePath,
      })

      return {
        contents: generated.code,
        sourceMap: generated.map,
      }
    },
  })
}
<commit_msg>:bug: Await on more addImports<commit_after>// @flow

import invariant from 'assert'
import generate from '@babel/generator'
import * as t from '@babel/types'
import { promisify } from 'util'
import { transform } from '@babel/core'
import { createFileTransformer, getChunk, getChunkHash, getFileImportHash } from 'pundle-api'

import pluginTransformNodeEnvInline from 'babel-plugin-transform-node-env-inline'

import pluginRemoveDeadNodes from './plugin-remove-dead-nodes'
import manifest from '../package.json'
import { getName } from './helpers'

const INJECTIONS = new Map([['timers', ['setImmediate', 'clearImmediate']], ['buffer', ['Buffer']], ['process', 'process']])
const INJECTIONS_NAMES = new Map()
INJECTIONS.forEach(function(names, sourceModule) {
  ;[].concat(names).forEach(function(name) {
    INJECTIONS_NAMES.set(name, sourceModule)
  })
})

const transformAsync = promisify(transform)

// TODO: have a config?
export default function({ transformCore }: { transformCore: boolean }) {
  return createFileTransformer({
    name: 'pundle-transformer-js',
    version: manifest.version,
    priority: 1000,
    async callback({ file, resolve, addImport, addChunk }) {
      if (file.format !== 'js') return null

      const promises = []
      const injectionNames = new Set()
      const { ast } = await transformAsync(typeof file.contents === 'string' ? file.contents : file.contents.toString(), {
        ast: true,
        code: false,
        babelrc: false,
        configFile: false,
        sourceMaps: false,
        filename: file.filePath,
        highlightCode: false,
        plugins: [
          pluginTransformNodeEnvInline,
          pluginRemoveDeadNodes,
          {
            visitor: {
              ImportDeclaration({ node }) {
                const { source } = node
                if (!t.isStringLiteral(source)) return
                promises.push(
                  resolve(source.value, source.loc).then(resolved => {
                    source.value = getFileImportHash(resolved)
                    return addImport(resolved)
                  }),
                )
              },
              CallExpression(path) {
                const { node } = path
                const { callee } = node
                const [arg] = node.arguments

                if (!t.isStringLiteral(arg)) return

                if (t.isImport(callee)) {
                  promises.push(
                    resolve(arg.value, arg.loc).then(resolved => {
                      const chunk = getChunk(resolved.format, null, resolved.filePath)
                      node.callee = t.memberExpression(t.identifier('require'), t.identifier('chunk'))
                      arg.value = getChunkHash(chunk)
                      return addChunk(chunk)
                    }),
                  )
                  return
                }

                if (!['require', 'require.resolve'].includes(getName(callee)) || path.scope.hasBinding('require')) {
                  return
                }

                // Handling require + require.resolve
                promises.push(
                  resolve(arg.value, arg.loc).then(resolved => {
                    arg.value = getFileImportHash(resolved)
                    return addImport(resolved)
                  }),
                )
              },
              ...(transformCore
                ? {
                    Identifier(path) {
                      const { node } = path
                      if (
                        INJECTIONS_NAMES.has(node.name) &&
                        !injectionNames.has(node.name) &&
                        path.isReferencedIdentifier() &&
                        !path.scope.hasBinding(node.name)
                      ) {
                        injectionNames.add(node.name)
                      }
                    },
                  }
                : {}),
            },
          },
        ],
        sourceType: 'module',
        parserOpts: {
          plugins: ['dynamicImport'],
        },
      })

      const injectionImports = new Map()
      injectionNames.forEach(item => {
        const sourceModule = INJECTIONS_NAMES.get(item)
        invariant(sourceModule, 'sourceModule for Injection was not found?')
        promises.push(
          resolve(sourceModule).then(resolved => {
            injectionImports.set(sourceModule, getFileImportHash(resolved))
            return addImport(resolved)
          }),
        )
      })

      await Promise.all(promises)

      if (injectionImports.size) {
        const wrapper = t.callExpression(t.functionExpression(null, [], t.blockStatement([])), [])
        injectionImports.forEach((resolved, importName) => {
          wrapper.arguments.push(t.callExpression(t.identifier('require'), [t.stringLiteral(resolved)]))
          let names = INJECTIONS.get(importName)
          if (typeof names === 'string') {
            wrapper.callee.params.push(t.identifier(names))
          } else if (Array.isArray(names)) {
            names = names.filter(item => injectionNames.has(item))
            const refName = `__$sb$pundle${importName}`
            wrapper.callee.params.push(t.identifier(refName))
            ast.program.body = [
              t.variableDeclaration(
                'var',
                names.map(name =>
                  t.variableDeclarator(t.identifier(name), t.memberExpression(t.identifier(refName), t.identifier(name))),
                ),
              ),
            ].concat(ast.program.body)
          }
        })
        wrapper.callee.body.body = ast.program.body
        wrapper.callee.body.directives = ast.program.directives
        ast.program.body = [t.expressionStatement(wrapper)]
        ast.program.directives = []
      }

      const generated = generate(ast, {
        sourceMaps: true,
        sourceFileName: file.filePath,
      })

      return {
        contents: generated.code,
        sourceMap: generated.map,
      }
    },
  })
}
<|endoftext|>


<commit_before>module.exports = function(grunt) {

	//Loads all grunt tasks matching the ['grunt-*', '@*/grunt-*'] patterns.
	require('load-grunt-tasks')(grunt);

	var now = new Date(); // Used for git commit message.

	grunt.initConfig({

		pkg: grunt.file.readJSON('package.json'),

		watch: {
			options: {
				livereload: true,
			},
			scss: {
				files: ['css/sass/*.scss', 'css/sass/**/*.scss'],
				tasks: ['compass', 'combine_mq', 'cssmin'],
				options: {
					spawn: false
				}
			},
			scripts: {
				files: ['js/config.js', 'js/modules/**/*.js'],
				tasks: ['jshint', 'requirejs']
			},
			src: {
				files: [
					'**/*.php',
					'**/*.css',
					'js/**/*.js',
					'css/sass/**/*.scss',
					'package.json',
					'**/*.png',
					'**/*.jpg',
					'**/*.gif',
					'Gruntfile.js',
					'!node_modules/*',
				],
				tasks: ['gitadd', 'gitcommit']
			}
		},

		gitadd: {
			task: {
				options: {
					force: true
				},
				files: {
					src: ['*']
				}
			}
		},

		gitcommit: {
			your_target: {
				options: {
					cwd: '../../',
					message: 'Repo updated on: ' + grunt.template.today()
				},
				files: [{
					src: ['**/**/**'],
					expand: true,
					cwd: 'themes/digitalportfolio'
				}]
			}
		},

		compass: {
			dist: {
				options: {
					config: 'config.rb',
					sassDir: 'css/sass',
					cssDir: 'css',
					environment: 'development'
				}
			}
		},

		combine_mq: {
			options: {
				beautify: true
			},
			main: {
				src: 'style.css',
				dest: 'style.css'
			}
		},

		cssmin: {
			target: {
				files: [{
					expand: true,
					cwd: 'css/sass',
					src: ['*.css']
				}]
			}
		},

		jshint: {
			all: ['Gruntfile.js']
		},

		requirejs: {
			compile: {
				options: {
					baseUrl: 'js',
					name: 'config',
					paths: {
						jquery: 'libraries/jquery-1.11.3.min',
						app: 'modules/app'
					},
					out: 'js/optimize.min.js'
				}
			}
		}
	});
	grunt.registerTask('default', [
		'grunt-contrib-grunt-git',
		'grunt-contrib-cssmin',
		'grunt-contrib-combine-mq',
		'grunt-contrib-compass',
		'grunt-contrib-jshint',
		'grunt-contrib-requirejs'
	]);
};<commit_msg>Repo updated on: Sun Dec 20 2015 14:01:06<commit_after>module.exports = function(grunt) {

	//Loads all grunt tasks matching the ['grunt-*', '@*/grunt-*'] patterns.
	require('load-grunt-tasks')(grunt);

	var now = new Date(); // Used for git commit message.

	grunt.initConfig({

		pkg: grunt.file.readJSON('package.json'),

		watch: {
			options: {
				livereload: true,
			},
			scss: {
				files: ['css/sass/*.scss', 'css/sass/**/*.scss'],
				tasks: ['compass', 'combine_mq', 'cssmin'],
				options: {
					spawn: false
				}
			},
			scripts: {
				files: ['js/config.js', 'js/modules/**/*.js', 'Gruntfile.js'],
				tasks: ['jshint', 'requirejs']
			},
			src: {
				files: [
					'**/*.php',
					'**/*.css',
					'js/**/*.js',
					'css/sass/**/*.scss',
					'package.json',
					'**/*.png',
					'**/*.jpg',
					'**/*.gif',
					'Gruntfile.js',
					'!node_modules/*',
				],
				tasks: ['gitadd', 'gitcommit']
			}
		},

		gitadd: {
			task: {
				options: {
					force: true
				},
				files: {
					src: ['*']
				}
			}
		},

		gitcommit: {
			your_target: {
				options: {
					cwd: '../../',
					message: 'Repo updated on: ' + grunt.template.today()
				},
				files: [{
					src: ['**/**/**'],
					expand: true,
					cwd: 'themes/digitalportfolio'
				}]
			}
		},

		compass: {
			dist: {
				options: {
					config: 'config.rb',
					sassDir: 'css/sass',
					cssDir: 'css',
					environment: 'development'
				}
			}
		},

		combine_mq: {
			options: {
				beautify: true
			},
			main: {
				src: 'style.css',
				dest: 'style.css'
			}
		},

		cssmin: {
			target: {
				files: [{
					expand: true,
					cwd: 'css/sass',
					src: ['*.css']
				}]
			}
		},

		jshint: {
			all: ['Gruntfile.js']
		},

		requirejs: {
			compile: {
				options: {
					baseUrl: 'js',
					name: 'config',
					paths: {
						jquery: 'libraries/jquery-1.11.3.min',
						app: 'modules/app'
					},
					out: 'js/optimize.min.js'
				}
			}
		}
	});
	grunt.registerTask('default', [
		'grunt-contrib-grunt-git',
		'grunt-contrib-cssmin',
		'grunt-contrib-combine-mq',
		'grunt-contrib-compass',
		'grunt-contrib-jshint',
		'grunt-contrib-requirejs'
	]);
};<|endoftext|>


<commit_before>/*
 * (C) Copyright 2016 o2r project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
'use strict';

var override = require('../config/custom-mime.json');
var Mimos = require('mimos');
var mime = new Mimos({override});

/**
 * Function to rewrite the basepath of a directory-tree listing.
 * It will recursively remove a number trunc of chars from the
 * beginning of tree.path and replace them with the string newpath.
 *
 * Also adds MIME type information to entries that are not folders (i. e.
 * have no children array).
 *
 * @param {json} tree file directory tree
 * @param {int} trunc number of chars to remove from the beginning of tree.path
 * @param {character} newpath replacement characters for removed chars
 * @return {json} the rewritten directory tree
 */
function rewriteTree(tree, trunc, newpath) {
  tree.path = tree.path.substring(trunc);
  tree.path = newpath + tree.path;
  if (tree.children) {
    tree.children.map(child => {
      return rewriteTree(child, trunc, newpath);
    });
  } else {
    var mimetype = mime.path(tree.path).type;
    tree.type = mimetype;
  }
  return tree;
}

module.exports = rewriteTree;
<commit_msg>play around with pdf page count<commit_after>/*
 * (C) Copyright 2016 o2r project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
'use strict';

var override = require('../config/custom-mime.json');
var Mimos = require('mimos');
var mime = new Mimos({ override });

/**
 * Function to rewrite the basepath of a directory-tree listing.
 * It will recursively remove a number trunc of chars from the
 * beginning of tree.path and replace them with the string newpath.
 *
 * Also adds MIME type information to entries that are not folders (i. e.
 * have no children array).
 *
 * @param {json} tree file directory tree
 * @param {int} trunc number of chars to remove from the beginning of tree.path
 * @param {character} newpath replacement characters for removed chars
 * @return {json} the rewritten directory tree
 */
function rewriteTree(tree, trunc, newpath) {
  tree.origPath = tree.path; // must be deleted later!
  tree.path = tree.path.substring(trunc);
  tree.path = newpath + tree.path;
  if (tree.children) {
    tree.children.map(child => {
      return rewriteTree(child, trunc, newpath);
    });

    return finish(tree);
  } else {
    var mimetype = mime.path(tree.path).type;
    tree.type = mimetype;

    //if (mimetype === 'application/pdf' && config.bagtainer.tree.count_pdf_pages) {
    //  var data = new Uint8Array(fs.readFileSync(tree.origPath));
    //  pdfjs.getDocument(data).then(function (doc) {
    //    tree.pages = doc.numPages;
    //    return finish(tree);
    //  });
    //} else {
    //  return finish(tree);
    //}

    return finish(tree);
  }
}

function finish(tree) {
  delete tree.origPath;
  return tree;
}

module.exports = rewriteTree;
<|endoftext|>


<commit_before>// Licensed to the Software Freedom Conservancy (SFC) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The SFC licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

import { Commands } from "../neo/models/Command";
import { registerCommand } from "./commandExecutor";
import { sendMessage } from "./communication";

function RunCommand(id, command, target, value, options) {
  return sendMessage(id, {
    action: "execute",
    command: {
      command,
      target,
      value
    },
    options
  });
}

class PluginManager {
  plugins = [];

  registerPlugin(plugin) {
    if (!this.hasPlugin(plugin.id)) {
      this.plugins.push(plugin);
      if (plugin.commands) {
        plugin.commands.forEach(({id, name}) => {
          Commands.addCommand(id, name);
          registerCommand(id, RunCommand.bind(undefined, plugin.id, id));
        });
      }
    } else {
      throw new Error("This plugin is already registered");
    }
  }

  hasPlugin(pluginId) {
    return !!this.plugins.find(p => p.id === pluginId);
  }

  emitMessage(message) {
    return Promise.all(this.plugins.map(plugin => (
      sendMessage(plugin.id, message).catch(() => (Promise.resolve()))
    )));
  }
}

export default new PluginManager();
<commit_msg>send the errors as well<commit_after>// Licensed to the Software Freedom Conservancy (SFC) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The SFC licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

import { Commands } from "../neo/models/Command";
import { registerCommand } from "./commandExecutor";
import { sendMessage } from "./communication";

function RunCommand(id, command, target, value, options) {
  return sendMessage(id, {
    action: "execute",
    command: {
      command,
      target,
      value
    },
    options
  });
}

class PluginManager {
  plugins = [];

  registerPlugin(plugin) {
    if (!this.hasPlugin(plugin.id)) {
      this.plugins.push(plugin);
      if (plugin.commands) {
        plugin.commands.forEach(({id, name}) => {
          Commands.addCommand(id, name);
          registerCommand(id, RunCommand.bind(undefined, plugin.id, id));
        });
      }
    } else {
      throw new Error("This plugin is already registered");
    }
  }

  hasPlugin(pluginId) {
    return !!this.plugins.find(p => p.id === pluginId);
  }

  emitMessage(message) {
    return Promise.all(this.plugins.map(plugin => (
      sendMessage(plugin.id, message).catch((err) => (Promise.resolve(err)))
    )));
  }
}

export default new PluginManager();
<|endoftext|>


<commit_before>/**
 * We.js route alias feature
 */
var we ;

var alias = {
  // alias cache
  cache: {},

  /**
   * Initialize we.js alias feature
   *
   * @param  {O}   we
   * @param  {Object}   we we.js
   * @param  {Function} cb callback
   */
  initialize: function initialize(w, cb) {
    we = w;
    alias.loadAndCacheAllALias().spread(function (results){
      for (var i = results.length - 1; i >= 0; i--) {
        alias.cache[results[i].target] = results[i];
      }

      cb();
    }).catch(cb);
  },

  /**
   * Load all alias in memory for use with sync methods
   *
   * @param  {Object}   we we.js
   * @param  {Function} cb callback
   * @return {Object} Sequelize query promisse
   */
  loadAndCacheAllALias: function loadAndCacheAllALias() {
    var we = require('../index.js');
    return we.db.defaultConnection.query('SELECT * FROM urlAlias');
  },

  /**
   * http handler to use in http.createServer
   */
  httpHandler: function httpHandler(req, res) {
    var we = require('../index.js');
    // only works with GET requests
    if (req.method != 'GET') return we.express.bind(this)(req, res);

    // skip alias
    for (var i = we.config.router.alias.excludePaths.length - 1; i >= 0; i--) {
     if (req.url.indexOf(we.config.router.alias.excludePaths[i]) === 0){
       return we.express.bind(this)(req, res);
     }
    }

    var urlParts = req.url.split(/[?#]/);
    var path = urlParts[0];

    if (!path) return we.express.bind(this)(req, res);

    we.db.models.urlAlias.findOne({
      where: {
        $or: {
          alias: path,
          target: path
        }
      }
    }).then(function (urlAlias) {
      if (urlAlias) {

        if (urlAlias.alias == path) {
          // is a a alias for current url

          // save old url
          req.urlBeforeAlias = req.url;
          // save the url alias record
          req.urlAlias = urlAlias;

          req.url = urlAlias.target;

          if (urlParts[1]) req.url += '?' + urlParts[1];

          we.log.verbose('ulrAlias set for: ' + path + ' to: '+ req.url);

          we.express.bind(this)(req, res);
        } else {
          // is a target how have alias then redirect to it
          res.setHeader('Cache-Control', 'public, max-age=345600');
          res.setHeader('Expires', new Date(Date.now() + 345600000).toUTCString());
          return res.redirect(307, urlAlias.alias);
        }
      } else {
        we.log.verbose('ulrAlias not found for:', path);
        // slug not found then continue with default express middlewares
        we.express.bind(this)(req, res);
      }
    }).catch(function errorInSlugHandler(err){
      we.log.error(err);
      res.status(500).end();
    });
  },

  /**
   * Check if path have a alias
   *
   * @param  {String} path
   * @return {String} Alias or null
   */
  forPath: function forPath(path) {
    if (alias.cache[path]) {
      return alias.cache[path].alias;
    } else {
      return null;
    }
  },

  /**
   * Check if a sequelize model class have alias
   *
   * @param  {Object} Model Sequelize model class
   * @return {Boolean}
   */
  modelHaveUrlAlias: function modelHaveUrlAlias(Model) {
    return Boolean (
      Model.options &&
      Model.options.classMethods &&
      Model.options.classMethods.urlAlias
    );
  },

  // Model hook functions
  //

  afterCreatedRecord: function(record, opts, done) {
    if (!we) return done();

    var aliasObj = opts.model.options.classMethods.urlAlias(record);

    if (!alias) return done();

    we.db.models.urlAlias.create(aliasObj)
    .then(function (alS) {
      we.log.verbose('New url alias:', alS.id);
      done();
    }).catch(function (err){
      we.log.error('Error in generate url alias:', err);
      done();
    });
  },

  afterUpdatedRecord: function(record, opts, done) {
    if (!we) return done();

    var p = record.getUrlPath();
    var aliasObj = opts.model.options.classMethods.urlAlias(record);

    if (alias.cache[p]) {
      // alias found the update
      if (alias.cache[p].alias == aliasObj.alias) {
        // salved alias is same as new then do nothing
        return done();
      }

      we.db.models.urlAlias.findById(alias.cache[p].id)
      .then(function(a){
        a.updateAttributes(aliasObj).then(function(){
          done();
        }).catch(function (err){
          we.log.error('Error in updateAttributes url alias:', err);
          done();
        });
      }).catch(function (err) {
        we.log.error('Error in update url alias:', err);
        done();
      });
    } else {
      // this model dont have a alias, the create one
      we.db.models.urlAlias.create(aliasObj)
      .then(function (alS) {
        we.log.verbose('New url alias:', alS.id);
        done();
      }).catch(function (err){
        we.log.error('Error in generate url alias for:', err);
        done();
      });
    }
  },

  afterDeleteRecord: function(record, opts, done) {
    if (!we) return done();

    var p = record.getUrlPath();

    if (alias.cache[p]) {
      we.db.models.urlAlias.findById(alias.cache[p].id)
      .then(function(a){
        a.destroy().then(function () {
          done();
        }).catch(function (err) {
          we.log.error('Error in delete url alias:', err);
          done();
        });
      }).catch(function (err) {
        we.log.error('Error in delete url alias:', err);
        done();
      });
    } else {
      done();
    }
  }
}

module.exports = alias;<commit_msg>fix url alias redirect to use http methods<commit_after>/**
 * We.js route alias feature
 */
var we ;

var alias = {
  // alias cache
  cache: {},

  /**
   * Initialize we.js alias feature
   *
   * @param  {O}   we
   * @param  {Object}   we we.js
   * @param  {Function} cb callback
   */
  initialize: function initialize(w, cb) {
    we = w;
    alias.loadAndCacheAllALias().spread(function (results){
      for (var i = results.length - 1; i >= 0; i--) {
        alias.cache[results[i].target] = results[i];
      }

      cb();
    }).catch(cb);
  },

  /**
   * Load all alias in memory for use with sync methods
   *
   * @param  {Object}   we we.js
   * @param  {Function} cb callback
   * @return {Object} Sequelize query promisse
   */
  loadAndCacheAllALias: function loadAndCacheAllALias() {
    var we = require('../index.js');
    return we.db.defaultConnection.query('SELECT * FROM urlAlias');
  },

  /**
   * http handler to use in http.createServer
   */
  httpHandler: function httpHandler(req, res) {
    var we = require('../index.js');
    // only works with GET requests
    if (req.method != 'GET') return we.express.bind(this)(req, res);

    // skip alias
    for (var i = we.config.router.alias.excludePaths.length - 1; i >= 0; i--) {
     if (req.url.indexOf(we.config.router.alias.excludePaths[i]) === 0){
       return we.express.bind(this)(req, res);
     }
    }

    var urlParts = req.url.split(/[?#]/);
    var path = urlParts[0];

    if (!path) return we.express.bind(this)(req, res);

    we.db.models.urlAlias.findOne({
      where: {
        $or: {
          alias: path,
          target: path
        }
      }
    }).then(function (urlAlias) {
      if (urlAlias) {

        if (urlAlias.alias == path) {
          // is a a alias for current url

          // save old url
          req.urlBeforeAlias = req.url;
          // save the url alias record
          req.urlAlias = urlAlias;

          req.url = urlAlias.target;

          if (urlParts[1]) req.url += '?' + urlParts[1];

          we.log.verbose('ulrAlias set for: ' + path + ' to: '+ req.url);

          we.express.bind(this)(req, res);
        } else {
          // is a target how have alias then redirect to it
          res.writeHead(307, {
            'Location': urlAlias.alias,
            'Content-Type': 'multipart/form-data',
            'Cache-Control':'public, max-age=345600',
            'Expires': new Date(Date.now() + 345600000).toUTCString()
          });
          return res.end();
        }
      } else {
        we.log.verbose('ulrAlias not found for:', path);
        // slug not found then continue with default express middlewares
        we.express.bind(this)(req, res);
      }
    }).catch(function errorInSlugHandler(err){
      we.log.error(err);
      res.status(500).end();
    });
  },

  /**
   * Check if path have a alias
   *
   * @param  {String} path
   * @return {String} Alias or null
   */
  forPath: function forPath(path) {
    if (alias.cache[path]) {
      return alias.cache[path].alias;
    } else {
      return null;
    }
  },

  /**
   * Check if a sequelize model class have alias
   *
   * @param  {Object} Model Sequelize model class
   * @return {Boolean}
   */
  modelHaveUrlAlias: function modelHaveUrlAlias(Model) {
    return Boolean (
      Model.options &&
      Model.options.classMethods &&
      Model.options.classMethods.urlAlias
    );
  },

  // Model hook functions
  //

  afterCreatedRecord: function(record, opts, done) {
    if (!we) return done();

    var aliasObj = opts.model.options.classMethods.urlAlias(record);

    if (!alias) return done();

    we.db.models.urlAlias.create(aliasObj)
    .then(function (alS) {
      we.log.verbose('New url alias:', alS.id);
      done();
    }).catch(function (err){
      we.log.error('Error in generate url alias:', err);
      done();
    });
  },

  afterUpdatedRecord: function(record, opts, done) {
    if (!we) return done();

    var p = record.getUrlPath();
    var aliasObj = opts.model.options.classMethods.urlAlias(record);

    if (alias.cache[p]) {
      // alias found the update
      if (alias.cache[p].alias == aliasObj.alias) {
        // salved alias is same as new then do nothing
        return done();
      }

      we.db.models.urlAlias.findById(alias.cache[p].id)
      .then(function(a){
        a.updateAttributes(aliasObj).then(function(){
          done();
        }).catch(function (err){
          we.log.error('Error in updateAttributes url alias:', err);
          done();
        });
      }).catch(function (err) {
        we.log.error('Error in update url alias:', err);
        done();
      });
    } else {
      // this model dont have a alias, the create one
      we.db.models.urlAlias.create(aliasObj)
      .then(function (alS) {
        we.log.verbose('New url alias:', alS.id);
        done();
      }).catch(function (err){
        we.log.error('Error in generate url alias for:', err);
        done();
      });
    }
  },

  afterDeleteRecord: function(record, opts, done) {
    if (!we) return done();

    var p = record.getUrlPath();

    if (alias.cache[p]) {
      we.db.models.urlAlias.findById(alias.cache[p].id)
      .then(function(a){
        a.destroy().then(function () {
          done();
        }).catch(function (err) {
          we.log.error('Error in delete url alias:', err);
          done();
        });
      }).catch(function (err) {
        we.log.error('Error in delete url alias:', err);
        done();
      });
    } else {
      done();
    }
  }
}

module.exports = alias;<|endoftext|>


<commit_before>// extracted from https://github.com/developit/propTypes README
function validateProps(props, propTypes, service) {
  for (const prop in propTypes) {
    if (propTypes.hasOwnProperty(prop)) {
      if (typeof propTypes[prop] !== 'function') {
        console.error(
          'Error: invalid prop type',
          prop,
          propTypes,
          (service && service.name) ? service.name : service
        );
      }
      const err = propTypes[prop](props, prop, service, 'prop');
      if (err) {
        console.warn(err);
        return false;
      }
    }
  }
  return true;
}

function getWebmiddleAncestors(webmiddle) {
  const ancestors = [];

  let currentWebmiddle = webmiddle;
  while (currentWebmiddle.parent) {
    currentWebmiddle = currentWebmiddle.parent;
    ancestors.push(currentWebmiddle);
  }

  return ancestors;
}

function getTopWebmiddle(webmiddle) {
  const ancestors = getWebmiddleAncestors(webmiddle);
  return (ancestors.length > 0) ? ancestors[ancestors.length - 1] : webmiddle;
}

// Two webmiddles are related if they are the same
// or if one is ancestor of the other
function areWebmiddlesRelated(first, second) {
  const firstAncestors = getWebmiddleAncestors(first);
  const secondAncestors = getWebmiddleAncestors(second);
  return (first === second) ||
         (firstAncestors.indexOf(second) !== -1 || secondAncestors.indexOf(first) !== -1);
}

// Call the service multiple times based on the "retries" prop.
async function callService(service, props) {
  try {
    const result = await service(props);
    return result;
  } catch (err) {
    let retries = props.options.retries;
    if (typeof retries === 'function') retries = retries(err);
    retries = retries || 0;

    if (retries === 0) { // < 0 for infinite retries
      throw err;
    }
    console.error((err instanceof Error) ? err.stack : err);
    console.log('Retries left:', (retries < 0) ? '(infinity)' : retries);
    return callService(service, {
      ...props,
      options: {
        ...props.options,
        retries: (retries < 0) ? retries : (retries - 1),
      },
    });
  }
}

export default async function callVirtual(virtual, options = {}) {
  const service = virtual.type;

  if (typeof service !== 'function') {
    return { result: virtual, webmiddle: this, linkedWebmiddle: null };
  }

  const webmiddle = service.webmiddle || this;

  const linkedWebmiddle = areWebmiddlesRelated(this, webmiddle) ? null : getTopWebmiddle(webmiddle);
  if (linkedWebmiddle) {
    // link (set temp parent)
    linkedWebmiddle.parent = this;
  }

  const props = {
    ...virtual.attributes,
    children: virtual.children,
    webmiddle,
    options,
  };
  // options prop
  const serviceOptions = (typeof service.options === 'function') ?
    service.options(props) : service.options;
  props.options = {
    ...options,
    ...(serviceOptions || {}),
  };

  if (service.propTypes) {
    validateProps(virtual.attributes, service.propTypes, service);
  }

  const result = await callService(service, props);

  return { result, webmiddle, linkedWebmiddle };
};
<commit_msg>webmiddle: allow options.retries as a function<commit_after>// extracted from https://github.com/developit/propTypes README
function validateProps(props, propTypes, service) {
  for (const prop in propTypes) {
    if (propTypes.hasOwnProperty(prop)) {
      if (typeof propTypes[prop] !== 'function') {
        console.error(
          'Error: invalid prop type',
          prop,
          propTypes,
          (service && service.name) ? service.name : service
        );
      }
      const err = propTypes[prop](props, prop, service, 'prop');
      if (err) {
        console.warn(err);
        return false;
      }
    }
  }
  return true;
}

function getWebmiddleAncestors(webmiddle) {
  const ancestors = [];

  let currentWebmiddle = webmiddle;
  while (currentWebmiddle.parent) {
    currentWebmiddle = currentWebmiddle.parent;
    ancestors.push(currentWebmiddle);
  }

  return ancestors;
}

function getTopWebmiddle(webmiddle) {
  const ancestors = getWebmiddleAncestors(webmiddle);
  return (ancestors.length > 0) ? ancestors[ancestors.length - 1] : webmiddle;
}

// Two webmiddles are related if they are the same
// or if one is ancestor of the other
function areWebmiddlesRelated(first, second) {
  const firstAncestors = getWebmiddleAncestors(first);
  const secondAncestors = getWebmiddleAncestors(second);
  return (first === second) ||
         (firstAncestors.indexOf(second) !== -1 || secondAncestors.indexOf(first) !== -1);
}

// Call the service multiple times based on the "retries" prop.
async function callService(service, props, tries = 1) {
  try {
    const result = await service(props);
    return result;
  } catch (err) {
    let retries = props.options.retries;
    if (typeof retries === 'function') retries = retries(err);
    retries = retries || 0;

    if (tries === retries + 1) { // retries < 0 for infinite retries
      throw err;
    }
    const retriesLeft = retries - (tries - 1);
    console.error((err instanceof Error) ? err.stack : err);
    console.log('Retries left:', (retriesLeft < 0) ? '(infinity)' : retriesLeft);
    return callService(service, props, tries + 1);
  }
}

export default async function callVirtual(virtual, options = {}) {
  const service = virtual.type;

  if (typeof service !== 'function') {
    return { result: virtual, webmiddle: this, linkedWebmiddle: null };
  }

  const webmiddle = service.webmiddle || this;

  const linkedWebmiddle = areWebmiddlesRelated(this, webmiddle) ? null : getTopWebmiddle(webmiddle);
  if (linkedWebmiddle) {
    // link (set temp parent)
    linkedWebmiddle.parent = this;
  }

  const props = {
    ...virtual.attributes,
    children: virtual.children,
    webmiddle,
    options,
  };
  // options prop
  const serviceOptions = (typeof service.options === 'function') ?
    service.options(props) : service.options;
  props.options = {
    ...options,
    ...(serviceOptions || {}),
  };

  if (service.propTypes) {
    validateProps(virtual.attributes, service.propTypes, service);
  }

  const result = await callService(service, props);

  return { result, webmiddle, linkedWebmiddle };
};
<|endoftext|>


<commit_before>/*!
 * yowl
 * Copyright(c) 2016 Brian Brunner
 * MIT Licensed
 */

'use strict';

/**
 * Module Dependencies
 * @private
 */

var debug = require('debug')('router');
var Route = require('./route');

/**
 * Initialize a new `Router` with the given `options`.
 *
 * @param {Object} options
 * @return {Router} which is a callable function
 * @public
 */

var proto = module.exports = function(options) {

  function router(context, event, next) {
    router.handle(context, event, next);
  }

  // mixin Router class functions
  router.__proto__ = proto;

  router.options = options;
  router.stack = [];

  return router;
};

/**
 * Dispatch a context, event into the router.
 * @private
 */

proto.handle = function handle(context, event, nextAfterDone) {
  var self = this;

  var idx = 0;
  var route_err = null;
  var stack = this.stack;
  var stack_unroll = [];

  next.unroll = next.done = unroll;
  next();

  function next(err, callback) {
    route_err = err;
    if (typeof callback === "function") {
      stack_unroll.push(callback);
    }

    var route;
    var should_run;

    while (should_run !== true && idx < stack.length) {
      route = stack[idx++];
      should_run = test_should_run(route, context, event);

      if (typeof should_run !== "boolean") {
        route_err = route_err || should_run;
      }

      if (should_run !== true) {
        continue;
      }

      if (route_err) {
        should_run = false;
        continue;
      }

    }

    if (should_run !== true) {
      unroll();
      return;
    }

    route.handle_interaction(context, event, next);
  }

  function unroll(err) {

    // capture any errors that we encounter rolling up
    if (err) {
      if (route_err) {
        err._previous = route_err;
      }
      route_err = err;
    }

    if (stack_unroll.length === 0) {
      nextAfterDone(route_err);
      return;
    }

    var fn = stack_unroll.pop();
    if (fn.length == 3) {
      fn(context, event, unroll);
    } else if (fn.length == 4) {
      fn(err, context, event, unroll);
    } else {
      unroll();
    }
  }

};

/**
 * Use the given middleware function, with an optional test parameter
 *
 * Test can be a boolean, a function or (in the future) an object. Test
 * is used to determine whether or not a function should be run.
 *
 * @public
 */

proto.use = function use(test, fn) {
  if (typeof fn === "undefined" && typeof test === "function") {
    fn = test;
    test = true;
  }

  if (typeof fn !== "function") {
    throw new TypeError('Router.use() requires a middleware function');
  }

  if (typeof fn.should_run === "function" &&
      typeof fn.handle === "function") {
    this.stack.push(fn);
  } else {
    var route = new Route(test, {}, fn);
    this.stack.push(route);
  }

  return this;
};

/**
 * Test if a route should be run.
 *
 * @param {Context} context
 * @param {Event} event
 * @param {Route} route
 * @private
 */

function test_should_run(route, context, event) {
  // TODO allow for test functions to be async
  try {
    return route.should_run(context, event);
  } catch (err) {
    return err;
  }
}
<commit_msg>If a route calls next.done, the router should propagate up that it's done<commit_after>/*!
 * yowl
 * Copyright(c) 2016 Brian Brunner
 * MIT Licensed
 */

'use strict';

/**
 * Module Dependencies
 * @private
 */

var debug = require('debug')('router');
var Route = require('./route');

/**
 * Initialize a new `Router` with the given `options`.
 *
 * @param {Object} options
 * @return {Router} which is a callable function
 * @public
 */

var proto = module.exports = function(options) {

  function router(context, event, next) {
    router.handle(context, event, next);
  }

  // mixin Router class functions
  router.__proto__ = proto;

  router.options = options;
  router.stack = [];

  return router;
};

/**
 * Dispatch a context, event into the router.
 * @private
 */

proto.handle = function handle(context, event, nextAfterDone) {
  var self = this;

  var idx = 0;
  var route_err = null;
  var stack = this.stack;
  var stack_unroll = [];

  // If a route calls "next.done" propagate up that we are done
  next.unroll = next.done = function() {
    // Don't sub out nextAfterDone for the root router
    if (typeof nextAfterDone.done === "function") {
      nextAfterDone = nextAfterDone.done;
    }
    unroll();
  };

  next();

  function next(err, callback) {
    route_err = err;
    if (typeof callback === "function") {
      stack_unroll.push(callback);
    }

    var route;
    var should_run;

    while (should_run !== true && idx < stack.length) {
      route = stack[idx++];
      should_run = test_should_run(route, context, event);

      if (typeof should_run !== "boolean") {
        route_err = route_err || should_run;
      }

      if (should_run !== true) {
        continue;
      }

      if (route_err) {
        should_run = false;
        continue;
      }

    }

    if (should_run !== true) {
      unroll();
      return;
    }

    route.handle_interaction(context, event, next);
  }

  function unroll(err) {

    // capture any errors that we encounter rolling up
    if (err) {
      if (route_err) {
        err._previous = route_err;
      }
      route_err = err;
    }

    if (stack_unroll.length === 0) {
      nextAfterDone(route_err);
      return;
    }

    var fn = stack_unroll.pop();
    if (fn.length == 3) {
      fn(context, event, unroll);
    } else if (fn.length == 4) {
      fn(err, context, event, unroll);
    } else {
      unroll();
    }
  }

};

/**
 * Use the given middleware function, with an optional test parameter
 *
 * Test can be a boolean, a function or (in the future) an object. Test
 * is used to determine whether or not a function should be run.
 *
 * @public
 */

proto.use = function use(test, fn) {
  if (typeof fn === "undefined" && typeof test === "function") {
    fn = test;
    test = true;
  }

  if (typeof fn !== "function") {
    throw new TypeError('Router.use() requires a middleware function');
  }

  if (typeof fn.should_run === "function" &&
      typeof fn.handle === "function") {
    this.stack.push(fn);
  } else {
    var route = new Route(test, {}, fn);
    this.stack.push(route);
  }

  return this;
};

/**
 * Test if a route should be run.
 *
 * @param {Context} context
 * @param {Event} event
 * @param {Route} route
 * @private
 */

function test_should_run(route, context, event) {
  // TODO allow for test functions to be async
  try {
    return route.should_run(context, event);
  } catch (err) {
    return err;
  }
}
<|endoftext|>


<commit_before>'use babel';

import RubberDuckyView from './rubber-ducky-view';
import { CompositeDisposable } from 'atom';

const encouragements = [
    "You can do it!",
    "Don't give up!",
    ":) Smiley face is rooting for you!",
    "I believe in you!",
    "Just keep swimming. -Dory",
    "BELIEVE!",
    "There are better times ahead.",
    "I believe that we will win!",
    "If this were easy, it wouldn't be as fun.",
    "Don't quit!",
    "Take a break. You deserve it!",
    "You go!",
    "You got this!",
]

const responses = [
  "Uh huh.",
  "That sounds right.",
  "You're a genius!",
  "Try it!",
  "You're right.",
  "Yeah.",
  "Why not?",
  "How did you come to that conclusion?",
  "I taught you well.",
  "I'm so proud of you.",
  "Hmmm yeah!",
  "Yes.",
  "Are you sure?",
]

export default {

  subscriptions: null,

  activate(state) {
    // Events subscribed to in atom's system can be easily cleaned up with a CompositeDisposable
    this.subscriptions = new CompositeDisposable();

    // Register command that toggles this view
    this.subscriptions.add(atom.commands.add('atom-workspace', {
      'rubber-ducky:encourage': () => this.encourage(),
      'rubber-ducky:respond': () => this.respond(),
      'rubber-ducky:draw': () => this.draw(),
    }));
  },

  encourage() {
    let editor
    if (editor = atom.workspace.getActiveTextEditor()) {
      editor.insertText(encouragements[Math.floor(Math.random() * encouragements.length)]);
    }
  },

  respond() {
    let editor
    if (editor = atom.workspace.getActiveTextEditor()) {
      editor.insertText(responses[Math.floor(Math.random() * encouragements.length)]);
    }
  },

  draw() {
    let editor
    if (editor = atom.workspace.getActiveTextEditor()) {
      editor.insertText(`    __` + "\n" +
                        `___( o)>` + "\n" +
                        `\ <_. )` + "\n" +
                         "`---'");
    }
  },

};
<commit_msg>debugging draw<commit_after>'use babel';

import RubberDuckyView from './rubber-ducky-view';
import { CompositeDisposable } from 'atom';

const encouragements = [
    "You can do it!",
    "Don't give up!",
    ":) Smiley face is rooting for you!",
    "I believe in you!",
    "Just keep swimming. -Dory",
    "BELIEVE!",
    "There are better times ahead.",
    "I believe that we will win!",
    "If this were easy, it wouldn't be as fun.",
    "Don't quit!",
    "Take a break. You deserve it!",
    "You go!",
    "You got this!",
]

const responses = [
  "Uh huh.",
  "That sounds right.",
  "You're a genius!",
  "Try it!",
  "You're right.",
  "Yeah.",
  "Why not?",
  "How did you come to that conclusion?",
  "I taught you well.",
  "I'm so proud of you.",
  "Hmmm yeah!",
  "Yes.",
  "Are you sure?",
]

export default {

  subscriptions: null,

  activate(state) {
    // Events subscribed to in atom's system can be easily cleaned up with a CompositeDisposable
    this.subscriptions = new CompositeDisposable();

    // Register command that toggles this view
    this.subscriptions.add(atom.commands.add('atom-workspace', {
      'rubber-ducky:encourage': () => this.encourage(),
      'rubber-ducky:respond': () => this.respond(),
      'rubber-ducky:draw': () => this.draw(),
    }));
  },

  encourage() {
    let editor
    if (editor = atom.workspace.getActiveTextEditor()) {
      editor.insertText(encouragements[Math.floor(Math.random() * encouragements.length)]);
    }
  },

  respond() {
    let editor
    if (editor = atom.workspace.getActiveTextEditor()) {
      editor.insertText(responses[Math.floor(Math.random() * encouragements.length)]);
    }
  },

  draw() {
    let editor
    if (editor = atom.workspace.getActiveTextEditor()) {
      editor.insertText("draw ducky");
    }
  },

};
<|endoftext|>


<commit_before>/**
 * Module dependencies.
 */

var notifier = require('democracyos-notifier')
var log = require('debug')('democracyos:server')
var config = require('lib/config')
var app = require('lib/boot')
var serverFactory = require('lib/server-factory')
var notifications = require('lib/notifications').notifier

module.exports = function listen (opts, callback) {
  callback = callback || noop

  var servers = serverFactory(app, opts)

  Promise.all([
    startWebServer(),
    startNotificationsService()
  ]).then(() => {
    callback()
  }).catch(callback)

  function startWebServer () {
    return Promise.all(servers.map((server) => {
      return new Promise((resolve, reject) => {
        server.listen(function (err) {
          if (err) return reject(err)
          log('Server started at port %s.', server.port)
          resolve()
        })
      })
    }))
  }

  function startNotificationsService (cb) {
    if (!notifications.embedded()) return true

    return new Promise((resolve, reject) => {
      var opts = {
        mongoUrl: config.mongoUrl,
        organizationName: config.notifications.mailer.name,
        organizationEmail: config.notifications.mailer.email,
        mailer: config.notifications.mailer,
        nodemailer: config.notifications.nodemailer,
        defaultLocale: config.locale,
        availableLocales: config.availableLocales
      }

      log('Starting embedded notifier...')
      notifier(opts, function (err) {
        if (err) {
          log('Unexpected error while starting embedded notifier', err)
          return reject(err)
        }

        log('Embedded notifier started.')

        resolve()
      })
    })
  }
}

function noop () {}
<commit_msg>update notifier 2.0 init<commit_after>/**
 * Module dependencies.
 */

var notifier = require('democracyos-notifier')
var log = require('debug')('democracyos:server')
var config = require('lib/config')
var app = require('lib/boot')
var serverFactory = require('lib/server-factory')
var notifications = require('lib/notifications').notifier

module.exports = function listen (opts, callback) {
  callback = callback || noop

  var servers = serverFactory(app, opts)

  Promise.all([
    startWebServer(),
    startNotificationsService()
  ]).then(() => {
    callback()
  }).catch(callback)

  function startWebServer () {
    return Promise.all(servers.map((server) => {
      return new Promise((resolve, reject) => {
        server.listen(function (err) {
          if (err) return reject(err)
          log('Server started at port %s.', server.port)
          resolve()
        })
      })
    }))
  }

  function startNotificationsService (cb) {
    if (!notifications.embedded()) return true
    var opts = {
      mongoUrl: config.mongoUrl,
      organizationName: config.notifications.mailer.name,
      organizationEmail: config.notifications.mailer.email,
      mailer: config.notifications.mailer,
      nodemailer: config.notifications.nodemailer,
      defaultLocale: config.locale,
      availableLocales: config.availableLocales
    }
    notifier.config.set(config.notifications)

    return notifier[config.notifications.url ? 'init' : 'start']()
  }
}

function noop () {}
<|endoftext|>


<commit_before>var express = require('express');
var derby = require('derby');
var racerBrowserChannel = require('racer-browserchannel');
var liveDbMongo = require('livedb-mongo');
var redis = require ('redis');
var MongoStore = require('connect-mongo')(express);
var app = require('../app');
var error = require('./error');

var expressApp = module.exports = express();

var mongoUrl = (
  env.MONGO_URL ||
  env.MONGOHQ_URL ||
  'mongodb://localhost:27017/project'
);

function redisClient(){
  var redisUrl, store;

  // Get Redis configuration
  if (process.env.REDIS_HOST) {
    store = redis.createClient(process.env.REDIS_PORT, process.env.REDIS_HOST);
    store.auth(process.env.REDIS_PASSWORD);
  } else if (process.env.REDISCLOUD_URL) {
    redisUrl = require('url').parse(process.env.REDISCLOUD_URL);
    store = redis.createClient(redisUrl.port, redisUrl.hostname);
    store.auth(redisUrl.auth.split(":")[1]);
  } else {
    store = redis.createClient();
  }

  store.select(process.env.REDIS_DB || 1);

  return store;
}

var store = derby.createStore({db: {
  db: liveDbMongo(mongoUrl + '?auto_reconnect', {safe: true})
, redis: redisClient()
}});

function createUserId(req, res, next) {
  var model = req.getModel();
  var userId = req.session.userId || (req.session.userId = model.id());
  model.set('_session.userId', userId);
  next();
}

expressApp
  .use(express.favicon())
  // Gzip dynamically
  .use(express.compress())
  // Respond to requests for application script bundles
  .use(app.scripts(store))
  // Serve static files from the public directory
  // .use(express.static(__dirname + '/../../public'))

  // Add browserchannel client-side scripts to model bundles created by store,
  // and return middleware for responding to remote client messages
  .use(racerBrowserChannel(store))
  // Add req.getModel() method
  .use(store.modelMiddleware())

  // Parse form data
  // .use(express.bodyParser())
  // .use(express.methodOverride())

  // Session middleware
  .use(express.cookieParser())
  .use(express.session({
    secret: process.env.SESSION_SECRET || 'YOUR SECRET HERE'
  , store: new MongoStore({url: mongoUrl, safe: true})
  }))
  .use(createUserId)

  // Create an express middleware from the app's routes
  .use(app.router())
  .use(expressApp.router)
  .use(error())

// SERVER-SIDE ROUTES //

expressApp.all('*', function(req, res, next) {
  next('404: ' + req.url);
});
<commit_msg>mongo url<commit_after>var express = require('express');
var derby = require('derby');
var racerBrowserChannel = require('racer-browserchannel');
var liveDbMongo = require('livedb-mongo');
var redis = require ('redis');
var MongoStore = require('connect-mongo')(express);
var app = require('../app');
var error = require('./error');

var expressApp = module.exports = express();

var mongoUrl = (
  process.env.MONGO_URL ||
  process.env.MONGOHQ_URL ||
  'mongodb://localhost:27017/project'
);

function redisClient(){
  var redisUrl, store;

  // Get Redis configuration
  if (process.env.REDIS_HOST) {
    store = redis.createClient(process.env.REDIS_PORT, process.env.REDIS_HOST);
    store.auth(process.env.REDIS_PASSWORD);
  } else if (process.env.REDISCLOUD_URL) {
    redisUrl = require('url').parse(process.env.REDISCLOUD_URL);
    store = redis.createClient(redisUrl.port, redisUrl.hostname);
    store.auth(redisUrl.auth.split(":")[1]);
  } else {
    store = redis.createClient();
  }

  store.select(process.env.REDIS_DB || 1);

  return store;
}

var store = derby.createStore({db: {
  db: liveDbMongo(mongoUrl + '?auto_reconnect', {safe: true})
, redis: redisClient()
}});

function createUserId(req, res, next) {
  var model = req.getModel();
  var userId = req.session.userId || (req.session.userId = model.id());
  model.set('_session.userId', userId);
  next();
}

expressApp
  .use(express.favicon())
  // Gzip dynamically
  .use(express.compress())
  // Respond to requests for application script bundles
  .use(app.scripts(store))
  // Serve static files from the public directory
  // .use(express.static(__dirname + '/../../public'))

  // Add browserchannel client-side scripts to model bundles created by store,
  // and return middleware for responding to remote client messages
  .use(racerBrowserChannel(store))
  // Add req.getModel() method
  .use(store.modelMiddleware())

  // Parse form data
  // .use(express.bodyParser())
  // .use(express.methodOverride())

  // Session middleware
  .use(express.cookieParser())
  .use(express.session({
    secret: process.env.SESSION_SECRET || 'YOUR SECRET HERE'
  , store: new MongoStore({url: mongoUrl, safe: true})
  }))
  .use(createUserId)

  // Create an express middleware from the app's routes
  .use(app.router())
  .use(expressApp.router)
  .use(error())

// SERVER-SIDE ROUTES //

expressApp.all('*', function(req, res, next) {
  next('404: ' + req.url);
});
<|endoftext|>


<commit_before>"use strict";

var _s = require('underscore.string')
  , logger = require('./logger.js').get('appium')
  , status = require('./status.js')
  , doRequest = require('../devices/common.js').doRequest
  , respondError = require('./responses.js').respondError
  , _ = require('underscore')
  , safely = require('./helpers').safely;

module.exports.shouldProxy = function (req) {
  if (req.device === null) return false;
  if (!req.device.isProxy) return false;

  var deviceAvoids = req.device.avoidProxy || [];

  var avoid = [
    ['POST', new RegExp('^/wd/hub/session$')]
    , ['DELETE', new RegExp('^/wd/hub/session/[^/]+$')]
  ].concat(deviceAvoids);
  var method = req.method.toUpperCase();
  var path = req.originalUrl;
  var shouldAvoid = false;

  // check for POST /execute mobile:
  if (method === 'POST' &&
      new RegExp('^/wd/hub/session/.+/execute$').test(path) &&
      _s.startsWith(req.body.script, "mobile: ")) {
    shouldAvoid = true;
  }

  _.each(avoid, function (pathToAvoid) {
    if (method === pathToAvoid[0] && pathToAvoid[1].exec(path)) {
      shouldAvoid = true;
    }
  });
  return !shouldAvoid;
};

module.exports.doProxy = function (req, res) {
  logger.debug("Proxying command to " + req.device.proxyHost + ":" +
               req.device.proxyPort);
  var sessRe = new RegExp('^/wd/hub/session/([^/]+)');
  var sessionRegxMatch = sessRe.exec(req.originalUrl);
  var newPath;
  // there might be no session id in the orig. req.
  if (sessionRegxMatch) {
    var origSessId = sessionRegxMatch[1];
    var sessId = req.device.proxySessionId ? req.device.proxySessionId :
                                             origSessId;
    newPath = req.originalUrl.replace(origSessId, sessId);
  } else {
    newPath = req.originalUrl;
  }
  var url = 'http://' + req.device.proxyHost + ':' + req.device.proxyPort +
            newPath;
  doRequest(url, req.method.toUpperCase(), req.body,
          req.headers['content-type'], function (err, response, body) {
    if (err) {
      return respondError(req, res, status.codes.UnknownError.code,
        "Did not successfully proxy server command");
    }
    if (body && body.value) {
      var resStatusCode = body.status;
      if (resStatusCode !== 0) {
        var resErrorMessage = body.value.message;
        return respondError(req, res, resStatusCode, resErrorMessage);
      }
    }

    var sbody = body ? JSON.stringify(body).slice(0, 10000) : body;

    logger.debug("Proxied response received with status " +
                 response.statusCode + ": " +
                 sbody);
    safely(req, function () {
      res.headers = response.headers;
      res.set('Content-Type', response.headers['content-type']);
      res.status(response.statusCode).send(body);
    });
  });
};
<commit_msg>fix failure to remap session id in proxied responses (fix #4702)<commit_after>"use strict";

var _s = require('underscore.string')
  , logger = require('./logger.js').get('appium')
  , status = require('./status.js')
  , doRequest = require('../devices/common.js').doRequest
  , respondError = require('./responses.js').respondError
  , _ = require('underscore')
  , safely = require('./helpers').safely;

module.exports.shouldProxy = function (req) {
  if (req.device === null) return false;
  if (!req.device.isProxy) return false;

  var deviceAvoids = req.device.avoidProxy || [];

  var avoid = [
    ['POST', new RegExp('^/wd/hub/session$')]
    , ['DELETE', new RegExp('^/wd/hub/session/[^/]+$')]
  ].concat(deviceAvoids);
  var method = req.method.toUpperCase();
  var path = req.originalUrl;
  var shouldAvoid = false;

  // check for POST /execute mobile:
  if (method === 'POST' &&
      new RegExp('^/wd/hub/session/.+/execute$').test(path) &&
      _s.startsWith(req.body.script, "mobile: ")) {
    shouldAvoid = true;
  }

  _.each(avoid, function (pathToAvoid) {
    if (method === pathToAvoid[0] && pathToAvoid[1].exec(path)) {
      shouldAvoid = true;
    }
  });
  return !shouldAvoid;
};

module.exports.doProxy = function (req, res) {
  logger.debug("Proxying command to " + req.device.proxyHost + ":" +
               req.device.proxyPort);
  var sessRe = new RegExp('^/wd/hub/session/([^/]+)');
  var sessionRegxMatch = sessRe.exec(req.originalUrl);
  var newPath, origSessId = null;
  // there might be no session id in the orig. req.
  if (sessionRegxMatch) {
    origSessId = sessionRegxMatch[1];
    var sessId = req.device.proxySessionId ? req.device.proxySessionId :
                                             origSessId;
    newPath = req.originalUrl.replace(origSessId, sessId);
  } else {
    newPath = req.originalUrl;
  }
  var url = 'http://' + req.device.proxyHost + ':' + req.device.proxyPort +
            newPath;
  doRequest(url, req.method.toUpperCase(), req.body,
          req.headers['content-type'], function (err, response, body) {
    if (err) {
      return respondError(req, res, status.codes.UnknownError.code,
        "Did not successfully proxy server command");
    }
    if (body && body.value) {
      var resStatusCode = body.status;
      if (resStatusCode !== 0) {
        var resErrorMessage = body.value.message;
        return respondError(req, res, resStatusCode, resErrorMessage);
      }
    }
    var sbody = body ? JSON.stringify(body).slice(0, 10000) : body;

    logger.debug("Proxied response received with status " +
                 response.statusCode + ": " +
                 sbody);

    // if the proxied response contains a sessionId that the downstream
    // driver has generated, we don't want to return that to the client.
    // Instead, return the id for the current appium session
    if (body && body.sessionId && origSessId) {
      body.sessionId = origSessId;
    }

    safely(req, function () {
      res.headers = response.headers;
      res.set('Content-Type', response.headers['content-type']);
      res.status(response.statusCode).send(body);
    });
  });
};
<|endoftext|>