<commit_before>const async = require("async");
const models = require("../../models");
const log = require('../utils/logger');
const queue = require('./queue');
const i18n = require('../utils/i18n');
const toJson = require('../utils/to_json');
const _ = require('lodash');

let airbrake = null;
if(process.env.AIRBRAKE_PROJECT_ID) {
  airbrake = require('../utils/airbrake');
}

const recountGroupFromPostId = (postId, callback) => {
  let postsCount = 0;
  let pointsCount = 0;

  models.Post.unscoped().find({
    where: { id: postId },
    attributes: ['id', 'group_id']
  }).then((post) => {
    let groupId = post.group_id;
    async.series([
      (seriesCallback) => {
        models.Post.findAll({
          where: {
            group_id: groupId
          }
        }).then(function (posts) {
          postsCount = posts.length;
          seriesCallback();
        }).catch((error) => {
          seriesCallback(error);
        });
      },
      (seriesCallback) => {
        models.Point.findAll({
          include: [
            {
              model: models.Post,
              where: {
                group_id: groupId
              }
            }
          ]
        }).then(function (posts) {
          pointsCount = posts.length;
          seriesCallback();
        }).catch((error) => {
          seriesCallback(error);
        });
      }
    ], (error) => {
      if (error) {
        callback(error);
      } else {
        models.Group.find({
          where: { id: groupId },
          attributes: ['id', 'community_id', 'counter_posts', 'counter_points']
        }).then((group) => {
          if (group) {
            group.counter_posts = postsCount;
            group.counter_points = pointsCount;
            group.save().then(() => {
              callback();
            }).catch((error) => {
              callback(error);
            });
          } else {
            log.warn("No group for update counters");
            callback();
          }
        }).catch((error) => {
          callback(error);
        });
      }
    });
  }).catch((error) => {
    callback(error);
  });
};

const resetCountForCommunityForGroup = (groupId, callback) => {
  let totalPosts=0, totalPoints = 0;
  models.Group.unscoped().find({
    where: { id: groupId },
    attributes: ['id', 'community_id']
  }).then((group) => {
    if (group) {
      let communityId = group.community_id;
      async.series([
        (seriesCallback) => {
          models.Group.findAll({
            where: { community_id: communityId },
            attributes: ['id', 'community_id','counter_points','counter_posts']
          }).then((groups) => {
            groups.forEach((group) => {
              if (group.counter_posts) {
                totalPosts+=group.counter_posts;
              }
              if (group.counter_points) {
                totalPoints+=group.counter_points;
              }
            });
            models.Community.update(
              { counter_posts: totalPosts, counter_points: totalPoints },
              { where: { id: communityId} }
            ).then(() => {
              seriesCallback();
            }).catch((error) => {
              seriesCallback(error)
            });
          }).catch((error) => {
            seriesCallback(error);
          });
        }
      ], (error) => {
        callback(error);
      });
    } else {
      callback();
    }
  }).catch((error) => {
    callback(error)
  });
};

let DeletionWorker = function () {};

const deletePointContent = (workPackage, callback) => {
  const pointId = workPackage.pointId;
  log.info('Starting Point Activities Delete', {pointId: pointId, context: 'ac-delete', userId: workPackage.userId});
  if (pointId) {
    async.series([
      (seriesCallback) => {
      if (!workPackage.skipActivities) {
        models.AcActivity.update(
          { deleted: true },
          { where: { point_id: pointId}}
        ).then(function (spread) {
          log.info('Point Activities Deleted', {pointId: pointId, numberDeleted: spread[0],context: 'ac-delete', userId: workPackage.userId});
          seriesCallback();
        }).catch(function (error) {
          seriesCallback(error);
        });
      } else {
        seriesCallback();
      }
      },
      (seriesCallback) => {
        models.PointQuality.update(
          { deleted: true },
          { where: { point_id: pointId}}
        ).then(function (spread) {
          log.info('Point Quality Deleted', {pointId: pointId, numberDeleted: spread[0],context: 'ac-delete', userId: workPackage.userId});
          seriesCallback();
        }).catch(function (error) {
          seriesCallback(error);
        });
      }
    ], (error) => {
      callback(error);
    });
  } else {
    callback("No pointId for deletePointContent");
  }
};

const deletePostContent = (workPackage, callback) => {
  const postId = workPackage.postId;
  log.info('Starting Post Activities Delete', {postId: postId, context: 'ac-delete', userId: workPackage.userId});
  if (postId) {
    async.series([
      (seriesCallback) => {
        if (!workPackage.skipActivities) {
          models.AcActivity.update(
            { deleted: true },
            { where: { post_id: postId}}
          ).then((spread) => {
            log.info('Post Activities Deleted', {postId: postId, numberDeleted: spread[0],context: 'ac-delete', userId: workPackage.userId});
            seriesCallback();
          }).catch((error) => {
            seriesCallback(error);
          });
        } else {
          seriesCallback();
        }
      },
      (seriesCallback) => {
        models.Point.findAll({
          attributes: ['id'],
          where: {
            post_id: postId
          }
        }).then((points) => {
          async.forEach(points, (point, innerCallback) => {
            deletePointContent(_.merge({pointId: point.id, skipActivities: true}, workPackage), innerCallback);
          }, (error) => {
            seriesCallback(error);
          })

        }).catch((error) => {
          seriesCallback(error);
        });
      },
      (seriesCallback) => {
        models.Point.update(
          { deleted: true },
          { where: { post_id: postId } }
        ).then((spread) => {
          log.info('Post Activities Points Deleted', {postId: postId, numberDeleted: spread[0],context: 'ac-delete', userId: workPackage.userId});
          seriesCallback();
        }).catch((error) => {
          seriesCallback(error);
        })
      },
      (seriesCallback) => {
        models.Endorsement.update(
          { deleted: true },
          { where: { post_id: postId } }
        ).then((spread) => {
          log.info('Post Endorsement Deleted', { postId: postId, numberDeleted: spread[0],context: 'ac-delete', userId: workPackage.userId});
          seriesCallback();
        }).catch((error) => {
          seriesCallback(error);
        })
      },
      (seriesCallback) => {
        if (workPackage.resetCounters) {
          models.Post.update(
            { counter_endorsements_up: 0, counter_endorsements_down: 0, counter_points: 0, counter_users: 0 },
            { where: { id: postId } }
          ).then(function () {
            log.info("Post reset counters for post");
            recountGroupFromPostId(postId, (error) => {
              seriesCallback(error);
            });
          }).catch((error) => {
            seriesCallback(error);
          });
        } else {
          seriesCallback();
        }
      }], (error) => {
        if (workPackage.useNotification) {
          models.Post.find({
            where: { id: postId },
            attributes: ['id', 'group_id'],
            include: [
              {
                model: models.Group,
                attributes: ['id','community_id'],
                include: [
                  {
                    model: models.Community,
                    attributes: ['id', 'domain_id']
                  }
                ]
              }
            ]
          }).then((post) => {
            const notificationType = error ? 'deletePostContentError' : 'deletePostContentDone';
            models.AcActivity.createActivity({
              type: 'activity.system.generalUserNotification',
              object: { type: notificationType, name: workPackage.postName, forwardToUser: true, offerReload: true },
              userId: workPackage.userId, postId: postId, groupId: post.Group.id, communityId: post.Group.Community.id,
              domainId: post.Group.Community.domain_id
            }, (subError) => {
              callback(error || subError);
            });
          }).catch((error) => {
            callback(error);
          });
        } else {
          callback(error);
        }
      }
    );
  } else {
    callback("No postId for deletePostContent");
  }
};

const deleteGroupContent = (workPackage, callback) => {
  const groupId = workPackage.groupId;
  let allPosts = null;
  log.info('Starting Group Activities Delete', {groupId: groupId, context: 'ac-delete', userId: workPackage.userId});
  if (groupId) {
    async.series([
      (seriesCallback) => {
        models.AcActivity.update(
          { deleted: true },
          { where: { group_id: groupId }}
        ).then((spread) => {
          log.info('Group Activities Deleted', {groupId: groupId, numberDeleted: spread[0],context: 'ac-delete', userId: workPackage.userId});
          seriesCallback();
        }).catch((error) => {
          seriesCallback(error);
        });
      },
      (seriesCallback) => {
        models.Post.findAll({
          attributes: ['id'],
          where: { group_id: groupId }
        }).then(function (posts) {
          async.forEach(posts, function (post, innerCallback) {
            deletePostContent(_.merge({postId: post.id, skipActivities: true, useNotification: false, resetCounters: false }, workPackage), innerCallback);
          }, (error) => {
            seriesCallback(error);
          });
        }).catch((error) => {
          seriesCallback(error);
        })
      },
      (seriesCallback) => {
        models.Post.update(
          { deleted: true },
          { where: { group_id: groupId } }
        ).then((spread) => {
          log.info('Group Activities Post Deleted', {groupId: groupId, numberDeleted: spread[0],context: 'ac-delete', userId: workPackage.userId});
          seriesCallback();
        }).catch((error) => {
          seriesCallback(error);
        })
      },
      (seriesCallback) => {
        if (workPackage.resetCounters) {
          models.Group.update(
            { counter_posts: 0, counter_points: 0, counter_users: 0 },
            { where: { id: groupId } }
          ).then(function () {
            log.info("Group reset counters for group");
            resetCountForCommunityForGroup(groupId, seriesCallback);
          }).catch((error) => {
            seriesCallback(error);
          });
        } else {
          seriesCallback();
        }
      }], (error) => {
        if (workPackage.useNotification) {
          models.Group.find({
            where: { id: groupId },
            attributes: ['id', 'community_id'],
            include: [
              {
                model: models.Community,
                attributes: ['id', 'domain_id']
              }
            ]
          }).then((group) => {
            const notificationType = error ? 'deleteGroupContentError' : 'deleteGroupContentDone';
            models.AcActivity.createActivity({
              type: 'activity.system.generalUserNotification',
              object: { type: notificationType, name: workPackage.groupName, forwardToUser: true, offerReload: true },
              userId: workPackage.userId, groupId: group.id, communityId: group.Community.id, domainId: group.Community.domain_id
            }, (subError) => {
              callback(error || subError);
            });
          }).catch((error) => {
            callback(error);
          });
        } else {
          callback(error);
        }
      }
    );
  } else {
    callback("No groupId for deleteGroupContent");
  }
};

const deleteCommunityContent = (workPackage, callback) => {
  const communityId = workPackage.communityId;
  log.info('Starting Community Activities Delete', {communityId: communityId, context: 'ac-delete', userId: workPackage.userId});
  if (communityId) {
    async.series([
      (seriesCallback) => {
        models.AcActivity.update(
          { deleted: true },
          { where: { community_id: communityId }}
        ).then(function (spread) {
          log.info('Community Activities Deleted', {communityId: communityId, numberDeleted: spread[0],context: 'ac-delete', userId: workPackage.userId});
          seriesCallback();
        }).catch(function (error) {
          seriesCallback(error);
        });
      },
      (seriesCallback) => {
        models.Group.findAll({
            attributes: ['id'],
            where: { community_id: communityId }
          }
        ).then(function (groups) {
          groups.forEach(function (group) {
            queue.create('process-deletion', { type: 'delete-group-content', userId: workPackage.userId, groupId: group.id }).priority('high').removeOnComplete(true).save();
          });
          seriesCallback();
        }).catch((error) => {
          seriesCallback(error);
        })
      },
      (seriesCallback) => {
        if (workPackage.resetCounters) {
          models.Community.update(
            { counter_posts: 0, counter_points: 0, counter_groups: 0, counter_users: 0 },
            { where: { id: communityId } }
          ).then(function () {
            log.info("Community reset counters for community");
            seriesCallback();
          }).catch((error) => {
            seriesCallback(error);
          })
        } else {
          seriesCallback();
        }
      },
      (seriesCallback) => {
        models.Group.update(
          { deleted: true },
          { where: { community_id: communityId } }
        ).then(function () {
          log.info("Community groups deleted");
          seriesCallback();
        }).catch((error) => {
          seriesCallback(error);
        })
      }], (error) => {
        if (workPackage.useNotification) {
          models.Community.find({
            where: { id: communityId },
            attributes: ['id', 'domain_id']
          }).then((community) => {
            const notificationType = error ? 'deleteCommunityContentError' : 'deleteCommunityContentDone';
            models.AcActivity.createActivity({
              type: 'activity.system.generalUserNotification',
              object: { type: notificationType, name: workPackage.communityName, forwardToUser: true, offerReload: true },
              userId: workPackage.userId, communityId: community.id, domainId: community.domain_id
            }, (subError) => {
              callback(error || subError);
            });
          }).catch((error) => {
            callback(error);
          });
        } else {
          callback(error);
        }
      }
    );
  } else {
    callback("No communityId for deleteCommunityContent");
  }
};

DeletionWorker.prototype.process = (workPackage, callback) => {
  switch(workPackage.type) {
    case 'delete-point-content':
      deletePointContent(workPackage, callback);
      break;
    case 'delete-post-content':
      deletePostContent(workPackage, callback);
      break;
    case 'delete-group-content':
      deleteGroupContent(workPackage, callback);
      break;
    case 'delete-community-content':
      deleteCommunityContent(workPackage, callback);
      break;
    default:
      callback("Unknown type for workPackage");
  }
};

module.exports = new DeletionWorker();
<commit_msg>Work on anonymizations<commit_after>const async = require("async");
const models = require("../../models");
const log = require('../utils/logger');
const queue = require('./queue');
const i18n = require('../utils/i18n');
const toJson = require('../utils/to_json');
const _ = require('lodash');

let airbrake = null;
if(process.env.AIRBRAKE_PROJECT_ID) {
  airbrake = require('../utils/airbrake');
}

const recountGroupFromPostId = (postId, callback) => {
  let postsCount = 0;
  let pointsCount = 0;

  models.Post.unscoped().find({
    where: { id: postId },
    attributes: ['id', 'group_id']
  }).then((post) => {
    if (post) {
      let groupId = post.group_id;
      async.series([
        (seriesCallback) => {
          models.Post.findAll({
            where: {
              group_id: groupId
            }
          }).then(function (posts) {
            postsCount = posts.length;
            seriesCallback();
          }).catch((error) => {
            seriesCallback(error);
          });
        },
        (seriesCallback) => {
          models.Point.findAll({
            include: [
              {
                model: models.Post,
                where: {
                  group_id: groupId
                }
              }
            ]
          }).then(function (posts) {
            pointsCount = posts.length;
            seriesCallback();
          }).catch((error) => {
            seriesCallback(error);
          });
        }
      ], (error) => {
        if (error) {
          callback(error);
        } else {
          models.Group.find({
            where: { id: groupId },
            attributes: ['id', 'community_id', 'counter_posts', 'counter_points']
          }).then((group) => {
            if (group) {
              group.counter_posts = postsCount;
              group.counter_points = pointsCount;
              group.save().then(() => {
                callback();
              }).catch((error) => {
                callback(error);
              });
            } else {
              log.warn("No group for update counters");
              callback();
            }
          }).catch((error) => {
            callback(error);
          });
        }
      });
    } else {
      log.warn("No post for update counters");
      callback();
    }
  }).catch((error) => {
    callback(error);
  });
};

const resetCountForCommunityForGroup = (groupId, callback) => {
  let totalPosts=0, totalPoints = 0;
  models.Group.unscoped().find({
    where: { id: groupId },
    attributes: ['id', 'community_id']
  }).then((group) => {
    if (group) {
      let communityId = group.community_id;
      async.series([
        (seriesCallback) => {
          models.Group.findAll({
            where: { community_id: communityId },
            attributes: ['id', 'community_id','counter_points','counter_posts']
          }).then((groups) => {
            groups.forEach((group) => {
              if (group.counter_posts) {
                totalPosts+=group.counter_posts;
              }
              if (group.counter_points) {
                totalPoints+=group.counter_points;
              }
            });
            models.Community.update(
              { counter_posts: totalPosts, counter_points: totalPoints },
              { where: { id: communityId} }
            ).then(() => {
              seriesCallback();
            }).catch((error) => {
              seriesCallback(error)
            });
          }).catch((error) => {
            seriesCallback(error);
          });
        }
      ], (error) => {
        callback(error);
      });
    } else {
      callback();
    }
  }).catch((error) => {
    callback(error)
  });
};

let DeletionWorker = function () {};

const deletePointContent = (workPackage, callback) => {
  const pointId = workPackage.pointId;
  log.info('Starting Point Activities Delete', {pointId: pointId, context: 'ac-delete', userId: workPackage.userId});
  if (pointId) {
    async.series([
      (seriesCallback) => {
      if (!workPackage.skipActivities) {
        models.AcActivity.update(
          { deleted: true },
          { where: { point_id: pointId}}
        ).then(function (spread) {
          log.info('Point Activities Deleted', {pointId: pointId, numberDeleted: spread[0],context: 'ac-delete', userId: workPackage.userId});
          seriesCallback();
        }).catch(function (error) {
          seriesCallback(error);
        });
      } else {
        seriesCallback();
      }
      },
      (seriesCallback) => {
        models.PointQuality.update(
          { deleted: true },
          { where: { point_id: pointId}}
        ).then(function (spread) {
          log.info('Point Quality Deleted', {pointId: pointId, numberDeleted: spread[0],context: 'ac-delete', userId: workPackage.userId});
          seriesCallback();
        }).catch(function (error) {
          seriesCallback(error);
        });
      }
    ], (error) => {
      callback(error);
    });
  } else {
    callback("No pointId for deletePointContent");
  }
};

const deletePostContent = (workPackage, callback) => {
  const postId = workPackage.postId;
  log.info('Starting Post Activities Delete', {postId: postId, context: 'ac-delete', userId: workPackage.userId});
  if (postId) {
    async.series([
      (seriesCallback) => {
        if (!workPackage.skipActivities) {
          models.AcActivity.update(
            { deleted: true },
            { where: { post_id: postId}}
          ).then((spread) => {
            log.info('Post Activities Deleted', {postId: postId, numberDeleted: spread[0],context: 'ac-delete', userId: workPackage.userId});
            seriesCallback();
          }).catch((error) => {
            seriesCallback(error);
          });
        } else {
          seriesCallback();
        }
      },
      (seriesCallback) => {
        models.Point.findAll({
          attributes: ['id'],
          where: {
            post_id: postId
          }
        }).then((points) => {
          async.forEach(points, (point, innerCallback) => {
            deletePointContent(_.merge({pointId: point.id, skipActivities: true}, workPackage), innerCallback);
          }, (error) => {
            seriesCallback(error);
          })

        }).catch((error) => {
          seriesCallback(error);
        });
      },
      (seriesCallback) => {
        models.Point.update(
          { deleted: true },
          { where: { post_id: postId } }
        ).then((spread) => {
          log.info('Post Activities Points Deleted', {postId: postId, numberDeleted: spread[0],context: 'ac-delete', userId: workPackage.userId});
          seriesCallback();
        }).catch((error) => {
          seriesCallback(error);
        })
      },
      (seriesCallback) => {
        models.Endorsement.update(
          { deleted: true },
          { where: { post_id: postId } }
        ).then((spread) => {
          log.info('Post Endorsement Deleted', { postId: postId, numberDeleted: spread[0],context: 'ac-delete', userId: workPackage.userId});
          seriesCallback();
        }).catch((error) => {
          seriesCallback(error);
        })
      },
      (seriesCallback) => {
        if (workPackage.resetCounters) {
          models.Post.update(
            { counter_endorsements_up: 0, counter_endorsements_down: 0, counter_points: 0, counter_users: 0 },
            { where: { id: postId } }
          ).then(function () {
            log.info("Post reset counters for post");
            recountGroupFromPostId(postId, (error) => {
              seriesCallback(error);
            });
          }).catch((error) => {
            seriesCallback(error);
          });
        } else {
          seriesCallback();
        }
      }], (error) => {
        if (workPackage.useNotification) {
          models.Post.find({
            where: { id: postId },
            attributes: ['id', 'group_id'],
            include: [
              {
                model: models.Group,
                attributes: ['id','community_id'],
                include: [
                  {
                    model: models.Community,
                    attributes: ['id', 'domain_id']
                  }
                ]
              }
            ]
          }).then((post) => {
            const notificationType = error ? 'deletePostContentError' : 'deletePostContentDone';
            models.AcActivity.createActivity({
              type: 'activity.system.generalUserNotification',
              object: { type: notificationType, name: workPackage.postName, forwardToUser: true, offerReload: true },
              userId: workPackage.userId, postId: postId, groupId: post.Group.id, communityId: post.Group.Community.id,
              domainId: post.Group.Community.domain_id
            }, (subError) => {
              callback(error || subError);
            });
          }).catch((error) => {
            callback(error);
          });
        } else {
          callback(error);
        }
      }
    );
  } else {
    callback("No postId for deletePostContent");
  }
};

const deleteGroupContent = (workPackage, callback) => {
  const groupId = workPackage.groupId;
  let allPosts = null;
  log.info('Starting Group Activities Delete', {groupId: groupId, context: 'ac-delete', userId: workPackage.userId});
  if (groupId) {
    async.series([
      (seriesCallback) => {
        models.AcActivity.update(
          { deleted: true },
          { where: { group_id: groupId }}
        ).then((spread) => {
          log.info('Group Activities Deleted', {groupId: groupId, numberDeleted: spread[0],context: 'ac-delete', userId: workPackage.userId});
          seriesCallback();
        }).catch((error) => {
          seriesCallback(error);
        });
      },
      (seriesCallback) => {
        models.Post.findAll({
          attributes: ['id'],
          where: { group_id: groupId }
        }).then(function (posts) {
          async.forEach(posts, function (post, innerCallback) {
            deletePostContent(_.merge({postId: post.id, skipActivities: true, useNotification: false, resetCounters: false }, workPackage), innerCallback);
          }, (error) => {
            seriesCallback(error);
          });
        }).catch((error) => {
          seriesCallback(error);
        })
      },
      (seriesCallback) => {
        models.Post.update(
          { deleted: true },
          { where: { group_id: groupId } }
        ).then((spread) => {
          log.info('Group Activities Post Deleted', {groupId: groupId, numberDeleted: spread[0],context: 'ac-delete', userId: workPackage.userId});
          seriesCallback();
        }).catch((error) => {
          seriesCallback(error);
        })
      },
      (seriesCallback) => {
        if (workPackage.resetCounters) {
          models.Group.update(
            { counter_posts: 0, counter_points: 0, counter_users: 0 },
            { where: { id: groupId } }
          ).then(function () {
            log.info("Group reset counters for group");
            resetCountForCommunityForGroup(groupId, seriesCallback);
          }).catch((error) => {
            seriesCallback(error);
          });
        } else {
          seriesCallback();
        }
      }], (error) => {
        if (workPackage.useNotification) {
          models.Group.find({
            where: { id: groupId },
            attributes: ['id', 'community_id'],
            include: [
              {
                model: models.Community,
                attributes: ['id', 'domain_id']
              }
            ]
          }).then((group) => {
            const notificationType = error ? 'deleteGroupContentError' : 'deleteGroupContentDone';
            models.AcActivity.createActivity({
              type: 'activity.system.generalUserNotification',
              object: { type: notificationType, name: workPackage.groupName, forwardToUser: true, offerReload: true },
              userId: workPackage.userId, groupId: group.id, communityId: group.Community.id, domainId: group.Community.domain_id
            }, (subError) => {
              callback(error || subError);
            });
          }).catch((error) => {
            callback(error);
          });
        } else {
          callback(error);
        }
      }
    );
  } else {
    callback("No groupId for deleteGroupContent");
  }
};

const deleteCommunityContent = (workPackage, callback) => {
  const communityId = workPackage.communityId;
  log.info('Starting Community Activities Delete', {communityId: communityId, context: 'ac-delete', userId: workPackage.userId});
  if (communityId) {
    async.series([
      (seriesCallback) => {
        models.AcActivity.update(
          { deleted: true },
          { where: { community_id: communityId }}
        ).then(function (spread) {
          log.info('Community Activities Deleted', {communityId: communityId, numberDeleted: spread[0],context: 'ac-delete', userId: workPackage.userId});
          seriesCallback();
        }).catch(function (error) {
          seriesCallback(error);
        });
      },
      (seriesCallback) => {
        models.Group.findAll({
            attributes: ['id'],
            where: { community_id: communityId }
          }
        ).then(function (groups) {
          groups.forEach(function (group) {
            queue.create('process-deletion', { type: 'delete-group-content', userId: workPackage.userId, groupId: group.id }).priority('high').removeOnComplete(true).save();
          });
          seriesCallback();
        }).catch((error) => {
          seriesCallback(error);
        })
      },
      (seriesCallback) => {
        if (workPackage.resetCounters) {
          models.Community.update(
            { counter_posts: 0, counter_points: 0, counter_groups: 0, counter_users: 0 },
            { where: { id: communityId } }
          ).then(function () {
            log.info("Community reset counters for community");
            seriesCallback();
          }).catch((error) => {
            seriesCallback(error);
          })
        } else {
          seriesCallback();
        }
      },
      (seriesCallback) => {
        models.Group.update(
          { deleted: true },
          { where: { community_id: communityId } }
        ).then(function () {
          log.info("Community groups deleted");
          seriesCallback();
        }).catch((error) => {
          seriesCallback(error);
        })
      }], (error) => {
        if (workPackage.useNotification) {
          models.Community.find({
            where: { id: communityId },
            attributes: ['id', 'domain_id']
          }).then((community) => {
            const notificationType = error ? 'deleteCommunityContentError' : 'deleteCommunityContentDone';
            models.AcActivity.createActivity({
              type: 'activity.system.generalUserNotification',
              object: { type: notificationType, name: workPackage.communityName, forwardToUser: true, offerReload: true },
              userId: workPackage.userId, communityId: community.id, domainId: community.domain_id
            }, (subError) => {
              callback(error || subError);
            });
          }).catch((error) => {
            callback(error);
          });
        } else {
          callback(error);
        }
      }
    );
  } else {
    callback("No communityId for deleteCommunityContent");
  }
};

DeletionWorker.prototype.process = (workPackage, callback) => {
  switch(workPackage.type) {
    case 'delete-point-content':
      deletePointContent(workPackage, callback);
      break;
    case 'delete-post-content':
      deletePostContent(workPackage, callback);
      break;
    case 'delete-group-content':
      deleteGroupContent(workPackage, callback);
      break;
    case 'delete-community-content':
      deleteCommunityContent(workPackage, callback);
      break;
    default:
      callback("Unknown type for workPackage");
  }
};

module.exports = new DeletionWorker();
<|endoftext|>


<commit_before>Ext.onReady(function(){
        var mainStore = Ext.create('Ext.data.Store', {
                    storeId: 'tutorials',
                    fields:[ 'S.No','Title', 'Link'],
                    data: [
                        { no:'1', title: 'What is Ext.js?', link: 'http://kyrierose.github.io/'}
                    ]
            });

                Ext.create('Ext.grid.Panel', {
                    title: 'Extjs Tutorials',
                    store: mainStore,
                    columns: [
                        { text: 'S.No', dataIndex: 'no' },
                        { text: 'Title', dataIndex: 'title', width:200 },
                        { text: 'Link', dataIndex: 'link' , width: 200},
                    ],
                    style: 'margin:0 auto;',
                    height: 700,
                    align: 'center',
                    listeners: {
                        itemclick: function(dv, record, item, index, e) {
                                var selectedRec = dv.getSelectionModel().getSelected();
                                alert(selectedRec.get('link'));
                                alert(selectedRec.get('Link'));//Will display text of name column of selected record

                                var row = dv.store.indexOf(selectedRec);
                                var gridstore = dv.getStore();
                                var rowdata = gridstore.data.items[row];
                                alert(rowdata.data['title']);
                                alert(rowdata.data['link']);
                                alert(rowdata.data['Link']);
                        }
                    },
                    width: "50%",
                    renderTo: Ext.getBody()
                });
});
<commit_msg>Update app.js<commit_after>Ext.onReady(function(){
        var mainStore = Ext.create('Ext.data.Store', {
                    storeId: 'tutorials',
                    fields:[ 'S.No','Title', 'Link'],
                    data: [
                        { no:'1', title: 'What is Ext.js?', link: 'http://kyrierose.github.io/'}
                    ]
            });

                Ext.create('Ext.grid.Panel', {
                    title: 'Extjs Tutorials',
                    store: mainStore,
                    columns: [
                        { text: 'S.No', dataIndex: 'no' },
                        { text: 'Title', dataIndex: 'title', width:200 },
                        { text: 'Link', dataIndex: 'link' , width: 200},
                    ],
                    style: 'margin:0 auto;',
                    height: 700,
                    align: 'center',
                    listeners: {
                        itemclick: function(dv, record, item, index, e) {
                                var selectedRecord = pi.getSelectionModel().getSelection()[0];
                                if(selectedRecord === null)
                                    alert("Select a tutorial.");
                                else{
                                    var row = pi.store.indexOf(selectedRecord);
                                    var gridstore = pi.getStore();
                                    var rowdata = gridstore.data.items[row];

                                    window.open(rowdata.data['link']);

                                }
                        }
                    },
                    width: "50%",
                    renderTo: Ext.getBody()
                });
});
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2019, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 * WSO2 Inc. 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 Joi from '@hapi/joi';
import API from 'AppData/api';

/**
 * Get the base error message for error types.
 * This error overrides the default error messages of joi and adds simple error messages.
 *
 * @param {string} errorType The joi error type
 * @return {string} simplified error message.
 * */
function getMessage(errorType) {
    switch (errorType) {
        case 'any.empty':
            return 'should not be empty';
        case 'string.regex.base':
            return 'should not contain spaces or special characters';
        default:
            return 'should not be empty';
    }
}

/*
* eslint validation rule for no-unused-vars has been disabled in this component.
* According to the Joi extension, it is required to provide required four input
* arguments to custom Joi schema validate function.
* Ref: https://hapi.dev/family/joi/?v=15.1.1#extendextension
*/
const roleSchema = Joi.extend(joi => ({
    base: joi.string(),
    name: 'systemRole',
    rules: [
        {
            name: 'role',
            validate(params, value, state, options) { // eslint-disable-line no-unused-vars
                const api = new API();
                return api.validateSystemRole(value);
            },
        },
    ],
}));

const scopeSchema = Joi.extend(joi => ({
    base: joi.string(),
    name: 'scopes',
    rules: [
        {
            name: 'scope',
            validate(params, value, state, options) { // eslint-disable-line no-unused-vars
                const api = new API();
                return api.validateScopeName(value);
            },
        },
    ],
}));

const userRoleSchema = Joi.extend(joi => ({
    base: joi.string(),
    name: 'userRole',
    rules: [
        {
            name: 'role',
            validate(params, value, state, options) { // eslint-disable-line no-unused-vars
                const api = new API();
                return api.validateUSerRole(value);
            },
        },
    ],
}));

const apiSchema = Joi.extend(joi => ({
    base: joi.string(),
    name: 'api',
    rules: [
        {
            name: 'isAPIParameterExist',
            validate(params, value, state, options) { // eslint-disable-line no-unused-vars
                const api = new API();
                return api.validateAPIParameter(value);
            },
        },
    ],
}));

const documentSchema = Joi.extend(joi => ({
    base: joi.object(),
    name: 'document',
    rules: [
        {
            name: 'isDocumentPresent',
            validate(params, value, state, options) { // eslint-disable-line no-unused-vars
                const api = new API();
                return api.validateDocumentExists(value.id, value.name);
            },
        },
    ],
}));

const definition = {
    apiName: Joi.string().regex(/^[^~!@#;:%^*()+={}|\\<>"',&/$]+$/).required().error((errors) => {
        const tmpErrors = [...errors];
        errors.forEach((err, index) => {
            const tmpError = { ...err };
            tmpError.message = 'API Name ' + getMessage(err.type);
            tmpErrors[index] = tmpError;
        });
        return tmpErrors;
    }),
    apiVersion: Joi.string().regex(/^[^~!@#;:%^*()+={}|\\<>"',&/$]+$/).required().error((errors) => {
        const tmpErrors = [...errors];
        errors.forEach((err, index) => {
            const tmpError = { ...err };
            tmpError.message = 'API Version ' + getMessage(err.type);
            tmpErrors[index] = tmpError;
        });
        return tmpErrors;
    }),
    apiContext: Joi.string().regex(/(?!.*\/t\/.*|.*\/t$)^[/a-zA-Z0-9/]{1,50}$/).required().error((errors) => {
        const tmpErrors = [...errors];
        errors.forEach((err, index) => {
            const tmpError = { ...err };
            tmpError.message = 'API Context ' + getMessage(err.type);
            tmpErrors[index] = tmpError;
        });
        return tmpErrors;
    }),
    role: roleSchema.systemRole().role(),
    scope: scopeSchema.scopes().scope(),
    url: Joi.string().uri(),
    userRole: userRoleSchema.userRole().role(),
    apiParameter: apiSchema.api().isAPIParameterExist(),
    apiDocument: documentSchema.document().isDocumentPresent(),
    operationVerbs: Joi.array().items(Joi.string()).min(1).unique(),
    operationTarget: Joi.string().required(),
    name: Joi.string().min(1).max(255),
    email: Joi.string().email({ tlds: true }).required(),
};

export default definition;
<commit_msg>Fix url validation with specific error message<commit_after>/*
 * Copyright (c) 2019, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 * WSO2 Inc. 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 Joi from '@hapi/joi';
import API from 'AppData/api';

/**
 * Get the base error message for error types.
 * This error overrides the default error messages of joi and adds simple error messages.
 *
 * @param {string} errorType The joi error type
 * @return {string} simplified error message.
 * */
function getMessage(errorType) {
    switch (errorType) {
        case 'any.empty':
            return 'should not be empty';
        case 'string.regex.base':
            return 'should not contain spaces or special characters';
        default:
            return 'should not be empty';
    }
}

/*
* eslint validation rule for no-unused-vars has been disabled in this component.
* According to the Joi extension, it is required to provide required four input
* arguments to custom Joi schema validate function.
* Ref: https://hapi.dev/family/joi/?v=15.1.1#extendextension
*/
const roleSchema = Joi.extend(joi => ({
    base: joi.string(),
    name: 'systemRole',
    rules: [
        {
            name: 'role',
            validate(params, value, state, options) { // eslint-disable-line no-unused-vars
                const api = new API();
                return api.validateSystemRole(value);
            },
        },
    ],
}));

const scopeSchema = Joi.extend(joi => ({
    base: joi.string(),
    name: 'scopes',
    rules: [
        {
            name: 'scope',
            validate(params, value, state, options) { // eslint-disable-line no-unused-vars
                const api = new API();
                return api.validateScopeName(value);
            },
        },
    ],
}));

const userRoleSchema = Joi.extend(joi => ({
    base: joi.string(),
    name: 'userRole',
    rules: [
        {
            name: 'role',
            validate(params, value, state, options) { // eslint-disable-line no-unused-vars
                const api = new API();
                return api.validateUSerRole(value);
            },
        },
    ],
}));

const apiSchema = Joi.extend(joi => ({
    base: joi.string(),
    name: 'api',
    rules: [
        {
            name: 'isAPIParameterExist',
            validate(params, value, state, options) { // eslint-disable-line no-unused-vars
                const api = new API();
                return api.validateAPIParameter(value);
            },
        },
    ],
}));

const documentSchema = Joi.extend(joi => ({
    base: joi.object(),
    name: 'document',
    rules: [
        {
            name: 'isDocumentPresent',
            validate(params, value, state, options) { // eslint-disable-line no-unused-vars
                const api = new API();
                return api.validateDocumentExists(value.id, value.name);
            },
        },
    ],
}));

const definition = {
    apiName: Joi.string().regex(/^[^~!@#;:%^*()+={}|\\<>"',&/$]+$/).required().error((errors) => {
        const tmpErrors = [...errors];
        errors.forEach((err, index) => {
            const tmpError = { ...err };
            tmpError.message = 'API Name ' + getMessage(err.type);
            tmpErrors[index] = tmpError;
        });
        return tmpErrors;
    }),
    apiVersion: Joi.string().regex(/^[^~!@#;:%^*()+={}|\\<>"',&/$]+$/).required().error((errors) => {
        const tmpErrors = [...errors];
        errors.forEach((err, index) => {
            const tmpError = { ...err };
            tmpError.message = 'API Version ' + getMessage(err.type);
            tmpErrors[index] = tmpError;
        });
        return tmpErrors;
    }),
    apiContext: Joi.string().regex(/(?!.*\/t\/.*|.*\/t$)^[/a-zA-Z0-9/]{1,50}$/).required().error((errors) => {
        const tmpErrors = [...errors];
        errors.forEach((err, index) => {
            const tmpError = { ...err };
            tmpError.message = 'API Context ' + getMessage(err.type);
            tmpErrors[index] = tmpError;
        });
        return tmpErrors;
    }),
    role: roleSchema.systemRole().role(),
    scope: scopeSchema.scopes().scope(),
    url: Joi.string().uri().error((errors) => {
        const tmpErrors = [...errors];
        errors.forEach((err, index) => {
            const tmpError = { ...err };
            tmpError.message = 'URL ' + getMessage(err.type);
            tmpErrors[index] = tmpError;
        });
        return tmpErrors;
    }),
    userRole: userRoleSchema.userRole().role(),
    apiParameter: apiSchema.api().isAPIParameterExist(),
    apiDocument: documentSchema.document().isDocumentPresent(),
    operationVerbs: Joi.array().items(Joi.string()).min(1).unique(),
    operationTarget: Joi.string().required(),
    name: Joi.string().min(1).max(255),
    email: Joi.string().email({ tlds: true }).required(),
};

export default definition;
<|endoftext|>


<commit_before>/*jslint browser: true, nomen: true */

(function (flow, $, girder) {
    'use strict';

    $(document).ready(function () {
        girder.apiRoot = '/girder/api/v1';
        var app = new flow.App({
            el: 'body'
        });

        // Lookup the ID of the analysis that we wish to perform.
        app.analysisName = "testSlowdown-app";
        girder.restRequest({
            path: 'resource/search',
            data: {
                q: app.analysisName,
                types: JSON.stringify(["item"])
            }
        }).done(function (results) {
            app.analysisId = results["item"][0]._id;
            app.readyToAnalyze();
            console.log(app.analysisId);
        });

        app.readyToAnalyze = function () {
            if ("tree" in this && "analysisId" in this) {
                d3.select("#analyze").classed('disabled', false);
            }
        };

        function getMissingSpecies() {

          app.nMiss=document.getElementById("nMissing").value.toString();
          if(app.nMiss=="") app.nMiss=0;
          console.log(app.nMiss)

        }
        // override upload function for simple mode
        flow.DatasetManagementView.prototype.upload = function (file) {
            var reader = new FileReader();

            reader.onload = _.bind(function (e) {
                var dataset = {
                        name: file.name,
                        data: e.target.result
                    },
                    extension = file.name.split('.'),
                    typeFormat;

                extension = extension[extension.length - 1];
                typeFormat = flow.getTypeFormatsFromExtension(extension)[0];
                typeFormat = {type: typeFormat.type, format: typeFormat.format};
                _.extend(dataset, typeFormat);
                dataset = new Backbone.Model(dataset);

                if (typeFormat.type == "tree") {
                    app.tree = dataset.get('data');
                    d3.select("#tree-name").html('Tree: ' + file.name + ' ');
                    $("#column-input").text("Click GO! to make plot...");
                }
                app.readyToAnalyze();

                this.datasets.off('add', null, 'set-collection').add(dataset);
            }, this);

            reader.readAsText(file);
        };

        $("#analyze").click(function() {
            $("#analyze").attr("disabled", "disabled");
            $("#analyze").text("Re-run");
            $("#notice").text("Performing analysis...");

            getMissingSpecies();

            var inputs = {
                tree:   {type: "tree",   format: "newick",           data: app.tree},
                NumberMissing: {type: "string", format: "text", data: app.nMiss}
            };

            var outputs = {
                lttdata: {type: "table",  format: "rows"},
                slowdownResult: {type: "table",  format: "rows"}
            };

            flow.performAnalysis(app.analysisId, inputs, outputs,
                _.bind(function (error, result) {
                    app.taskId = result._id;
                    setTimeout(_.bind(app.checkResult, app), 1000);
                }, app));

        app.checkResult = function () {
            var check_url = '/item/' + this.analysisId + '/flow/' + this.taskId + '/status'
            girder.restRequest({path: check_url}).done(_.bind(function (result) {
                console.log(result.status);
                if (result.status === 'SUCCESS') {
                    // get result data
                    var result_url = '/item/' + this.analysisId + '/flow/' + this.taskId + '/result'
                    girder.restRequest({path: result_url}).done(_.bind(function (data) {
                        app.lttCoords = data.result.lttdata.data;
                        app.slowdownResult = data.result.slowdownResult;
                        console.log(app.slowdownResult);

                        // render results
                        var newRow = $('

');
                        $(".main-content").append(newRow);
                        newRow.append('
');

                        var result = $('

');
                        newRow.append(result);
                        result.append("
Results:<\h2>");

                        result.append("Analysis type: ");
                        result.append("MCCR Gamma test from Pybus and Harvey 2000

");

                        result.append("Number of species: ", data.result.slowdownResult.data.rows[0]["nSpecies"], "
");
                        result.append("Number of missing taxa: ", data.result.slowdownResult.data.rows[0]["NumberMissing"], "
");
                        result.append("Gamma statistic: ", data.result.slowdownResult.data.rows[0]["gam"].toFixed(2), "
");
                        result.append("Critical value of gamma: ", data.result.slowdownResult.data.rows[0]["criticalValue"].toFixed(2), "
");
                        result.append("P: ", data.result.slowdownResult.data.rows[0]["P"].toFixed(4), "
");

                        function getCol(matrix, col){
                            var column = [];
                            for(var i=0; i<matrix.length; i++){
                               column.push(matrix[i][col]);
                            }
                            return column;
                         }

                        var N = getCol(app.lttCoords.rows, "N");
                        var time = getCol(app.lttCoords.rows, "time")

                        console.log(N);
                        console.log(time);

                        var trace1 = {
                          x: time,
                          y: N,
                          mode: 'lines',
                          type: 'scatter'
                        };

                        var data = [trace1];

                        var layout = {
                          title:'LTT plot',
                          xaxis: {
                            title: 'Time before present',
                            showgrid: false,
                            zeroline: false,
                            nticks: 10
                          },
                          yaxis: {
                            title: 'Number of reconstructed lineages',
                            type: 'log',
                            showgrid: false,
                            zeroline: false,
                            nticks: 4
                          },
                        };

                        Plotly.newPlot('ltt-plot', data, layout);

                        $("#analyze").removeAttr("disabled");
                        $("#notice").text("Analysis succeeded!");

                    }, this));

                } else if (result.status === 'FAILURE') {
                    $("#analyze").removeAttr("disabled");
                    $("#notice").text("Analysis failed. " + result.message);
                } else {
                    setTimeout(_.bind(this.checkResult, this), 1000);
                }
            }, this));
        };

        });

        $("#help").click(function() {
            $("#upload").popover({
                title: 'Step #1',
                content: 'Upload your table (csv or tsv) and tree (newick) here',
                placement: 'bottom',
                trigger: 'manual'
            });
            $("#upload").popover('toggle');
            $("#analyze").popover({
                title: 'Step #3',
                content: 'Click on the "Go!" button',
                placement: 'bottom',
                trigger: 'manual'
            });
            $("#analyze").popover('toggle');

        });

        $("#table-preview").click(function() {
            toggleInputTablePreview();
        });

        app.render();
    });
}(window.flow, window.$, window.girder));
<commit_msg>string<commit_after>/*jslint browser: true, nomen: true */

(function (flow, $, girder) {
    'use strict';

    $(document).ready(function () {
        girder.apiRoot = '/girder/api/v1';
        var app = new flow.App({
            el: 'body'
        });

        // Lookup the ID of the analysis that we wish to perform.
        app.analysisName = "testSlowdown-app";
        girder.restRequest({
            path: 'resource/search',
            data: {
                q: app.analysisName,
                types: JSON.stringify(["item"])
            }
        }).done(function (results) {
            app.analysisId = results["item"][0]._id;
            app.readyToAnalyze();
            console.log(app.analysisId);
        });

        app.readyToAnalyze = function () {
            if ("tree" in this && "analysisId" in this) {
                d3.select("#analyze").classed('disabled', false);
            }
        };

        function getMissingSpecies() {

          app.nMiss=document.getElementById("nMissing").value.toString();
          if(app.nMiss=="") app.nMiss="0";
          console.log(app.nMiss)

        }
        // override upload function for simple mode
        flow.DatasetManagementView.prototype.upload = function (file) {
            var reader = new FileReader();

            reader.onload = _.bind(function (e) {
                var dataset = {
                        name: file.name,
                        data: e.target.result
                    },
                    extension = file.name.split('.'),
                    typeFormat;

                extension = extension[extension.length - 1];
                typeFormat = flow.getTypeFormatsFromExtension(extension)[0];
                typeFormat = {type: typeFormat.type, format: typeFormat.format};
                _.extend(dataset, typeFormat);
                dataset = new Backbone.Model(dataset);

                if (typeFormat.type == "tree") {
                    app.tree = dataset.get('data');
                    d3.select("#tree-name").html('Tree: ' + file.name + ' ');
                    $("#column-input").text("Click GO! to make plot...");
                }
                app.readyToAnalyze();

                this.datasets.off('add', null, 'set-collection').add(dataset);
            }, this);

            reader.readAsText(file);
        };

        $("#analyze").click(function() {
            $("#analyze").attr("disabled", "disabled");
            $("#analyze").text("Re-run");
            $("#notice").text("Performing analysis...");

            getMissingSpecies();

            var inputs = {
                tree:   {type: "tree",   format: "newick",           data: app.tree},
                NumberMissing: {type: "string", format: "text", data: app.nMiss}
            };

            var outputs = {
                lttdata: {type: "table",  format: "rows"},
                slowdownResult: {type: "table",  format: "rows"}
            };

            flow.performAnalysis(app.analysisId, inputs, outputs,
                _.bind(function (error, result) {
                    app.taskId = result._id;
                    setTimeout(_.bind(app.checkResult, app), 1000);
                }, app));

        app.checkResult = function () {
            var check_url = '/item/' + this.analysisId + '/flow/' + this.taskId + '/status'
            girder.restRequest({path: check_url}).done(_.bind(function (result) {
                console.log(result.status);
                if (result.status === 'SUCCESS') {
                    // get result data
                    var result_url = '/item/' + this.analysisId + '/flow/' + this.taskId + '/result'
                    girder.restRequest({path: result_url}).done(_.bind(function (data) {
                        app.lttCoords = data.result.lttdata.data;
                        app.slowdownResult = data.result.slowdownResult;
                        console.log(app.slowdownResult);

                        // render results
                        var newRow = $('

');
                        $(".main-content").append(newRow);
                        newRow.append('
');

                        var result = $('

');
                        newRow.append(result);
                        result.append("
Results:<\h2>");

                        result.append("Analysis type: ");
                        result.append("MCCR Gamma test from Pybus and Harvey 2000

");

                        result.append("Number of species: ", data.result.slowdownResult.data.rows[0]["nSpecies"], "
");
                        result.append("Number of missing taxa: ", data.result.slowdownResult.data.rows[0]["NumberMissing"], "
");
                        result.append("Gamma statistic: ", data.result.slowdownResult.data.rows[0]["gam"].toFixed(2), "
");
                        result.append("Critical value of gamma: ", data.result.slowdownResult.data.rows[0]["criticalValue"].toFixed(2), "
");
                        result.append("P: ", data.result.slowdownResult.data.rows[0]["P"].toFixed(4), "
");

                        function getCol(matrix, col){
                            var column = [];
                            for(var i=0; i<matrix.length; i++){
                               column.push(matrix[i][col]);
                            }
                            return column;
                         }

                        var N = getCol(app.lttCoords.rows, "N");
                        var time = getCol(app.lttCoords.rows, "time")

                        console.log(N);
                        console.log(time);

                        var trace1 = {
                          x: time,
                          y: N,
                          mode: 'lines',
                          type: 'scatter'
                        };

                        var data = [trace1];

                        var layout = {
                          title:'LTT plot',
                          xaxis: {
                            title: 'Time before present',
                            showgrid: false,
                            zeroline: false,
                            nticks: 10
                          },
                          yaxis: {
                            title: 'Number of reconstructed lineages',
                            type: 'log',
                            showgrid: false,
                            zeroline: false,
                            nticks: 4
                          },
                        };

                        Plotly.newPlot('ltt-plot', data, layout);

                        $("#analyze").removeAttr("disabled");
                        $("#notice").text("Analysis succeeded!");

                    }, this));

                } else if (result.status === 'FAILURE') {
                    $("#analyze").removeAttr("disabled");
                    $("#notice").text("Analysis failed. " + result.message);
                } else {
                    setTimeout(_.bind(this.checkResult, this), 1000);
                }
            }, this));
        };

        });

        $("#help").click(function() {
            $("#upload").popover({
                title: 'Step #1',
                content: 'Upload your table (csv or tsv) and tree (newick) here',
                placement: 'bottom',
                trigger: 'manual'
            });
            $("#upload").popover('toggle');
            $("#analyze").popover({
                title: 'Step #3',
                content: 'Click on the "Go!" button',
                placement: 'bottom',
                trigger: 'manual'
            });
            $("#analyze").popover('toggle');

        });

        $("#table-preview").click(function() {
            toggleInputTablePreview();
        });

        app.render();
    });
}(window.flow, window.$, window.girder));
<|endoftext|>


<commit_before>module.exports = function (RED) {
    "use strict";
    var coap = require("coap");

    // A node red node that sets up a local coap server
    function CoapServerNode(n) {
        // Create a RED node
        RED.nodes.createNode(this, n);
        var node = this;

        // Store local copies of the node configuration (as defined in the .html)
        node.options = {};
        node.options.name = n.name;
        node.options.port = n.port;
        node.options.ipv6 = n.ipv6;

        node._inputNodes = []; // collection of "coap in" nodes that represent coap resources

        // Setup node-coap server and start
        var serverSettings = {};
        if (node.options.ipv6) {
            serverSettings.type = "udp6";
        } else {
            serverSettings.type = "udp4";
        }
        node.server = new coap.createServer(serverSettings);
        node.server.on("request", function (req, res) {
            node.handleRequest(req, res);
            res.on("error", function (err) {
                node.log("server error");
                node.log(err);
            });
        });
        node.server.listen(node.options.port, function () {
            //console.log('server started');
            node.log("CoAP Server Started");
        });

        node.on("close", function () {
            node._inputNodes = [];
            node.server.close();
        });
    }
    RED.nodes.registerType("coap-server", CoapServerNode);

    CoapServerNode.prototype.registerInputNode = function (/*Node*/ resource) {
        var exists = false;
        for (var i = 0; i < this._inputNodes.length; i++) {
            if (
                this._inputNodes[i].options.url == resource.options.url &&
                this._inputNodes[i].options.method == resource.options.method
            ) {
                exists = true;

                //TODO: Does this have any effect? Should show the error in the frontend somehow? Some kind of status bar?
                this.error(
                    "Node with the specified URL and Method already exists!"
                );
            }
        }
        if (!exists) {
            this._inputNodes.push(resource);
        }
    };

    CoapServerNode.prototype.handleRequest = function (req, res) {
        //TODO: Check if there are any matching resource. If the resource is .well-known return the resource directory to the client
        var matchResource = false;
        var matchMethod = false;
        for (var i = 0; i < this._inputNodes.length; i++) {
            if (this._inputNodes[i].options.url == req.url) {
                matchResource = true;
                if (this._inputNodes[i].options.method == req.method) {
                    matchMethod = true;
                    var inNode = this._inputNodes[i];
                    inNode.send({ req: req, res: res });
                }
            }
        }
        if (!matchResource) {
            res.code = "4.04";
            return res.end();
        }

        if (!matchMethod) {
            res.code = "4.05";
            return res.end();
        }
    };

    function CoapInNode(n) {
        RED.nodes.createNode(this, n);

        //copy "coap in" node configuration locally
        this.options = {};
        this.options.method = n.method;
        this.options.name = n.name;
        this.options.server = n.server;
        this.options.url = n.url.charAt(0) == "/" ? n.url : "/" + n.url;

        this.serverConfig = RED.nodes.getNode(this.options.server);

        if (this.serverConfig) {
            this.serverConfig.registerInputNode(this);
        } else {
            this.error("Missing server configuration");
        }
    }
    RED.nodes.registerType("coap in", CoapInNode);
};
<commit_msg>Change object on which the error method is called<commit_after>module.exports = function (RED) {
    "use strict";
    var coap = require("coap");

    // A node red node that sets up a local coap server
    function CoapServerNode(n) {
        // Create a RED node
        RED.nodes.createNode(this, n);
        var node = this;

        // Store local copies of the node configuration (as defined in the .html)
        node.options = {};
        node.options.name = n.name;
        node.options.port = n.port;
        node.options.ipv6 = n.ipv6;

        node._inputNodes = []; // collection of "coap in" nodes that represent coap resources

        // Setup node-coap server and start
        var serverSettings = {};
        if (node.options.ipv6) {
            serverSettings.type = "udp6";
        } else {
            serverSettings.type = "udp4";
        }
        node.server = new coap.createServer(serverSettings);
        node.server.on("request", function (req, res) {
            node.handleRequest(req, res);
            res.on("error", function (err) {
                node.log("server error");
                node.log(err);
            });
        });
        node.server.listen(node.options.port, function () {
            //console.log('server started');
            node.log("CoAP Server Started");
        });

        node.on("close", function () {
            node._inputNodes = [];
            node.server.close();
        });
    }
    RED.nodes.registerType("coap-server", CoapServerNode);

    CoapServerNode.prototype.registerInputNode = function (/*Node*/ resource) {
        var exists = false;
        for (var i = 0; i < this._inputNodes.length; i++) {
            if (
                this._inputNodes[i].options.url == resource.options.url &&
                this._inputNodes[i].options.method == resource.options.method
            ) {
                exists = true;

                //TODO: Does this have any effect? Should show the error in the frontend somehow? Some kind of status bar?
                node.error(
                    "Node with the specified URL and Method already exists!"
                );
            }
        }
        if (!exists) {
            this._inputNodes.push(resource);
        }
    };

    CoapServerNode.prototype.handleRequest = function (req, res) {
        //TODO: Check if there are any matching resource. If the resource is .well-known return the resource directory to the client
        var matchResource = false;
        var matchMethod = false;
        for (var i = 0; i < this._inputNodes.length; i++) {
            if (this._inputNodes[i].options.url == req.url) {
                matchResource = true;
                if (this._inputNodes[i].options.method == req.method) {
                    matchMethod = true;
                    var inNode = this._inputNodes[i];
                    inNode.send({ req: req, res: res });
                }
            }
        }
        if (!matchResource) {
            res.code = "4.04";
            return res.end();
        }

        if (!matchMethod) {
            res.code = "4.05";
            return res.end();
        }
    };

    function CoapInNode(n) {
        RED.nodes.createNode(this, n);

        //copy "coap in" node configuration locally
        this.options = {};
        this.options.method = n.method;
        this.options.name = n.name;
        this.options.server = n.server;
        this.options.url = n.url.charAt(0) == "/" ? n.url : "/" + n.url;

        this.serverConfig = RED.nodes.getNode(this.options.server);

        if (this.serverConfig) {
            this.serverConfig.registerInputNode(this);
        } else {
            node.error("Missing server configuration");
        }
    }
    RED.nodes.registerType("coap in", CoapInNode);
};
<|endoftext|>


<commit_before>(function() {
  // cached super methods
  var _route = Backbone.History.prototype.route;
  var _getFragment = Backbone.History.prototype.getFragment;
  var _start = Backbone.History.prototype.start;
  var _checkUrl = Backbone.History.prototype.checkUrl;
  var _navigate = Backbone.History.prototype.navigate;
  var _extractParameters = Backbone.Router.prototype._extractParameters;

  // If we are in hash mode figure out if we are on a browser that is hit by 63777 and 85881
  //     https://bugs.webkit.org/show_bug.cgi?id=63777
  var _useReplaceState = /WebKit\/([\d.]+)/.exec(navigator.userAgent) && window.history.replaceState,

      // Some variants of android will not replace the location history properly, causing issues
      // on back navigation. Hack around the replace API making it two step for these cases.
      //
      // This is likely tied to a specific Webkit version but sticking to the environments that we
      // know this occurs in rather than trying to track down the webkit version that this might
      // be impacting and possibly getting it wrong.
      //    https://bugs.webkit.org/show_bug.cgi?id=85881
      _fakeReplace = /Android\s+([\d.]+)/.exec(navigator.userAgent) && (parseFloat(RegExp.$1) < 4.2);

  // pattern to recognize state index in hash
  var hashStrip = /^(?:#|%23)*\d*(?:#|%23)*/;
  // Cached regex for index extraction from the hash
  var indexMatch = /^(?:#|%23)*(\d+)(?:#|%23)/;

  _.extend(Backbone.Router.prototype, {
    // the direction index will be exposed on the parameters as 'direction'
    _extractParameters: function() {
      var params = _extractParameters.apply(this, arguments);
      params = params || [];
      var history = Backbone.history;
      if (history._trackDirection) {
        var oldIndex = history._directionIndex;
        history._directionIndex = history.loadIndex();
        params.direction = history._directionIndex - oldIndex;
      }
      return params;
    }
  });

  _.extend(Backbone.History.prototype, {
    // Get the location of the current route within the backbone history.
    // This should be considered a hint
    // Returns -1 if history is unknown or disabled
    getIndex : function() {
      return this._directionIndex;
    },

    getFragment : function(/* fragment, forcePushState */) {
      var rtn = _getFragment.apply(this, arguments);
      return rtn && rtn.replace(hashStrip, '');
    },

    start: function(/* options */) {
      var rtn = _start.apply(this, arguments);

      this._fakeReplace = _fakeReplace && !this.options.noReplaceHack;

      // Direction tracking setup
      this._trackDirection  = !!this.options.trackDirection;
      if (this._trackDirection) {
        var loadedIndex = this.loadIndex();
        this._directionIndex  = loadedIndex || window.history.length;
        this._state = {index: this._directionIndex};

        // If we are tracking direction ensure that we have a direction field to play with
        if (!loadedIndex) {
          var loc = window.location;
          if (!this._hasPushState) {
            loc.replace(loc.pathname + (loc.search || '') + '#' + this._directionIndex + '#' + this.fragment);
          } else {
            window.history.replaceState({index: this._directionIndex}, document.title, loc);
          }
        }
      }
      return rtn;
    },

    checkUrl : function(e) {
      var fromIframe = this.getFragment() == this.fragment && this.iframe;
      var loadedIndex = this.loadIndex(fromIframe && this.iframe.location.hash);
      var navigateOptions = {
        trigger: false,
        replace: !loadedIndex,
        forceIndex: loadedIndex || this._directionIndex + 1
      };
      _checkUrl.call(this, e, navigateOptions);
    },

    // The options object can contain `trigger: true` if you wish to have the
    // route callback be fired (not usually desirable), or `replace: true`, if
    // you wish to modify the current URL without adding an entry to the history.
    navigate: function(fragment, options) {
      if (this._ignoreChange) {
        this._pendingNavigate = _.bind(this.navigate, this, fragment, options);
        return;
      }

      if (!options || options === true) {
        options = {trigger: options};
      }
      var newIndex;
      if (this._trackDirection) {
        newIndex = options.forceIndex || (this._directionIndex + (options.replace ? 0 : 1));
      }
      if (this._hasPushState) {
        options.state = {index: newIndex};
      } else {
        if (this._trackDirection) {
          fragment = newIndex + '#' + fragment;
        }
      }
      _navigate.call(this, fragment, options);
    },

    _updateHash: function(location, frag, replace) {
      var base = location.toString().replace(/(javascript:|#).*$/, '') + '#';
      if (replace) {
        if (_useReplaceState) {
          window.history.replaceState({}, document.title, base + frag);
        } else {
          location.replace(base + frag);
        }
      } else {
        location.hash = frag;
      }
    },

    // Pulls the direction index out of the state or hash
    loadIndex : function(fragmentOverride) {
      if (!this._trackDirection) {
        return;
      }
      if (!fragmentOverride && this._hasPushState) {
        return (this._state && this._state.index) || 0;
      } else {
        var match = indexMatch.exec(fragmentOverride || window.location.hash);
        return (match && parseInt(match[1], 10)) || 0;
      }
    },

    route: function (route, callback) {
      return _route.call(this, route, _.bind(function(fragment) {
        var ignore = this._ignoreChange;
        if (_.isFunction(ignore)) {
          ignore = !this._ignoreChange(fragment, route);
        }

        if (ignore) {
          this._ignoreChange = false;
          this._directionIndex = Backbone.history.loadIndex();
          this._pendingNavigate && setTimeout(Backbone.history._pendingNavigate, 0);
          this._pendingNavigate = undefined;
        } else {
          callback && callback.apply(this, arguments);
        }
      }, this));
    },

    back : function(triggerRoute) {
      this.go(-1, triggerRoute);
    },

    foward : function(triggerRoute) {
      this.go(1, triggerRoute);
    },

    go : function(count, triggerRoute) {
      this._ignoreChange = _.isFunction(triggerRoute) ? triggerRoute : !triggerRoute;
      window.history.go(count);
    },

    /**
     * Hack to work around iframes that make have screwed with our history state.
     * Really one should choose not to use iframes but sometimes you must, even in 2013....
     */
    stepOut: function(options) {
      options = options || {};

      var stepLimit = options.stepLimit || 10,
          routeLimit,
          desiredRoute,
          iter = 0,
          timeout;

      if (options.view) {
        options.view.$('iframe').remove();
      }

      if (_.isString(options.trigger)) {
        // If a string is passed then we will ensure that they land on the desired route one way
        // or the other
        desiredRoute = options.trigger;
        routeLimit = options.routeLimit || 1;

        options.trigger = function(fragment) {
          routeLimit--;

          if (fragment === desiredRoute) {
            return true;
          } else if (routeLimit <= 0) {
            Backbone.history.navigate(desiredRoute, {replace: true, trigger: true});
            setTimeout(function() {
              options.callback && options.callback(desiredRoute, false);
            }, 10);
          } else {
            setTimeout(step, 10);
          }
        };
      }

      // General flow here is to try to do a back navigation and wait for a backbone event to trigger
      // If one does not within a given timeout then repeat.
      function step() {
        iter++;

        // Timeout before as some envs actually have a sync callback for back. (Android 2.x notably)
        timeout = setTimeout(function() {
          if (iter > stepLimit) {
            options.callback && options.callback(false);
          } else {
            step();
          }
        }, 100);

        Backbone.history.back(function(fragment) {
          clearTimeout(timeout);

          var trigger = _.isFunction(options.trigger) ? options.trigger.apply(this, arguments) : options.trigger;
          if (trigger || !desiredRoute) {
            options.callback && options.callback(fragment, !!trigger);
          }
          return trigger;
        });
      }
      step();
    }
  });

}());
<commit_msg>Perform fake replace when necesary<commit_after>(function() {
  // cached super methods
  var _route = Backbone.History.prototype.route;
  var _getFragment = Backbone.History.prototype.getFragment;
  var _start = Backbone.History.prototype.start;
  var _checkUrl = Backbone.History.prototype.checkUrl;
  var _navigate = Backbone.History.prototype.navigate;
  var _extractParameters = Backbone.Router.prototype._extractParameters;

  // If we are in hash mode figure out if we are on a browser that is hit by 63777 and 85881
  //     https://bugs.webkit.org/show_bug.cgi?id=63777
  var _useReplaceState = /WebKit\/([\d.]+)/.exec(navigator.userAgent) && window.history.replaceState,

      // Some variants of android will not replace the location history properly, causing issues
      // on back navigation. Hack around the replace API making it two step for these cases.
      //
      // This is likely tied to a specific Webkit version but sticking to the environments that we
      // know this occurs in rather than trying to track down the webkit version that this might
      // be impacting and possibly getting it wrong.
      //    https://bugs.webkit.org/show_bug.cgi?id=85881
      _fakeReplace = /Android\s+([\d.]+)/.exec(navigator.userAgent) && (parseFloat(RegExp.$1) < 4.2);

  // pattern to recognize state index in hash
  var hashStrip = /^(?:#|%23)*\d*(?:#|%23)*/;
  // Cached regex for index extraction from the hash
  var indexMatch = /^(?:#|%23)*(\d+)(?:#|%23)/;

  _.extend(Backbone.Router.prototype, {
    // the direction index will be exposed on the parameters as 'direction'
    _extractParameters: function() {
      var params = _extractParameters.apply(this, arguments);
      params = params || [];
      var history = Backbone.history;
      if (history._trackDirection) {
        var oldIndex = history._directionIndex;
        history._directionIndex = history.loadIndex();
        params.direction = history._directionIndex - oldIndex;
      }
      return params;
    }
  });

  _.extend(Backbone.History.prototype, {
    // Get the location of the current route within the backbone history.
    // This should be considered a hint
    // Returns -1 if history is unknown or disabled
    getIndex : function() {
      return this._directionIndex;
    },

    getFragment : function(/* fragment, forcePushState */) {
      var rtn = _getFragment.apply(this, arguments);
      return rtn && rtn.replace(hashStrip, '');
    },

    start: function(/* options */) {
      var rtn = _start.apply(this, arguments);

      this._fakeReplace = _fakeReplace && !this.options.noReplaceHack;

      // Direction tracking setup
      this._trackDirection  = !!this.options.trackDirection;
      if (this._trackDirection) {
        var loadedIndex = this.loadIndex();
        this._directionIndex  = loadedIndex || window.history.length;
        this._state = {index: this._directionIndex};

        // If we are tracking direction ensure that we have a direction field to play with
        if (!loadedIndex) {
          var loc = window.location;
          if (!this._hasPushState) {
            loc.replace(loc.pathname + (loc.search || '') + '#' + this._directionIndex + '#' + this.fragment);
          } else {
            window.history.replaceState({index: this._directionIndex}, document.title, loc);
          }
        }
      }
      return rtn;
    },

    checkUrl : function(e) {
      var fromIframe = this.getFragment() == this.fragment && this.iframe;
      var loadedIndex = this.loadIndex(fromIframe && this.iframe.location.hash);
      var navigateOptions = {
        trigger: false,
        replace: !loadedIndex,
        forceIndex: loadedIndex || this._directionIndex + 1
      };
      _checkUrl.call(this, e, navigateOptions);
    },

    // The options object can contain `trigger: true` if you wish to have the
    // route callback be fired (not usually desirable), or `replace: true`, if
    // you wish to modify the current URL without adding an entry to the history.
    navigate: function(fragment, options) {
      if (this._ignoreChange) {
        this._pendingNavigate = _.bind(this.navigate, this, fragment, options);
        return;
      } else if (options &&options.replace && this._fakeReplace) {
        // Under older Android environment we have to back out and then trigger a route in two
        // operations rather than an automic replace operation. Without this flow does not operate
        // correctly:
        //  1. Trigger #foo
        //  2. Replace #bar
        //  3. Trigger #baz
        //
        // This will trigger all of the proper routes but the history for this case will actually
        // be #foo, #baz rather than the desired #bar, #baz, causing unexpected navigation.
        options = _.omit(options, 'replace');
        options._replace = true;
        this._pendingNavigate = _.bind(this.navigate, this, fragment, options);
        this.back();
        return;
      }

      if (!options || options === true) {
        options = {trigger: options};
      }
      var newIndex;
      if (this._trackDirection) {
        newIndex = options.forceIndex || (this._directionIndex + (options.replace ? 0 : 1));

        if (options._replace) {
          // The step back mucked with our direction tracker. Update.
          this._directionIndex++;
        }
      }
      if (this._hasPushState) {
        options.state = {index: newIndex};
      } else {
        if (this._trackDirection) {
          fragment = newIndex + '#' + fragment;
        }
      }
      _navigate.call(this, fragment, options);
    },

    _updateHash: function(location, frag, replace) {
      var base = location.toString().replace(/(javascript:|#).*$/, '') + '#';
      if (replace) {
        if (_useReplaceState) {
          window.history.replaceState({}, document.title, base + frag);
        } else {
          location.replace(base + frag);
        }
      } else {
        location.hash = frag;
      }
    },

    // Pulls the direction index out of the state or hash
    loadIndex : function(fragmentOverride) {
      if (!this._trackDirection) {
        return;
      }
      if (!fragmentOverride && this._hasPushState) {
        return (this._state && this._state.index) || 0;
      } else {
        var match = indexMatch.exec(fragmentOverride || window.location.hash);
        return (match && parseInt(match[1], 10)) || 0;
      }
    },

    route: function (route, callback) {
      return _route.call(this, route, _.bind(function(fragment) {
        var ignore = this._ignoreChange;
        if (_.isFunction(ignore)) {
          ignore = !this._ignoreChange(fragment, route);
        }

        if (ignore) {
          this._ignoreChange = false;
          this._directionIndex = Backbone.history.loadIndex();
          this._pendingNavigate && setTimeout(Backbone.history._pendingNavigate, 0);
          this._pendingNavigate = undefined;
        } else {
          callback && callback.apply(this, arguments);
        }
      }, this));
    },

    back : function(triggerRoute) {
      this.go(-1, triggerRoute);
    },

    foward : function(triggerRoute) {
      this.go(1, triggerRoute);
    },

    go : function(count, triggerRoute) {
      this._ignoreChange = _.isFunction(triggerRoute) ? triggerRoute : !triggerRoute;
      window.history.go(count);
    },

    /**
     * Hack to work around iframes that make have screwed with our history state.
     * Really one should choose not to use iframes but sometimes you must, even in 2013....
     */
    stepOut: function(options) {
      options = options || {};

      var stepLimit = options.stepLimit || 10,
          routeLimit,
          desiredRoute,
          iter = 0,
          timeout;

      if (options.view) {
        options.view.$('iframe').remove();
      }

      if (_.isString(options.trigger)) {
        // If a string is passed then we will ensure that they land on the desired route one way
        // or the other
        desiredRoute = options.trigger;
        routeLimit = options.routeLimit || 1;

        options.trigger = function(fragment) {
          routeLimit--;

          if (fragment === desiredRoute) {
            return true;
          } else if (routeLimit <= 0) {
            Backbone.history.navigate(desiredRoute, {replace: true, trigger: true});
            setTimeout(function() {
              options.callback && options.callback(desiredRoute, false);
            }, 10);
          } else {
            setTimeout(step, 10);
          }
        };
      }

      // General flow here is to try to do a back navigation and wait for a backbone event to trigger
      // If one does not within a given timeout then repeat.
      function step() {
        iter++;

        // Timeout before as some envs actually have a sync callback for back. (Android 2.x notably)
        timeout = setTimeout(function() {
          if (iter > stepLimit) {
            options.callback && options.callback(false);
          } else {
            step();
          }
        }, 100);

        Backbone.history.back(function(fragment) {
          clearTimeout(timeout);

          var trigger = _.isFunction(options.trigger) ? options.trigger.apply(this, arguments) : options.trigger;
          if (trigger || !desiredRoute) {
            options.callback && options.callback(fragment, !!trigger);
          }
          return trigger;
        });
      }
      step();
    }
  });

}());
<|endoftext|>


<commit_before>Meteor.startup(function () {
  smtp = {
    username: 'root',
    server:   'localhost',  // eg: mail.gandi.net
    port: 25
  }
  process.env.MAIL_URL = 'smtp://' + encodeURIComponent(smtp.username) '@' + encodeURIComponent(smtp.server) + ':' + smtp.port;
});

<commit_msg>strings are hard<commit_after>Meteor.startup(function () {
  smtp = {
    username: 'root',
    server:   'localhost',  // eg: mail.gandi.net
    port: 25
  }
  process.env.MAIL_URL = 'smtp://' + encodeURIComponent(smtp.username) + '@' + encodeURIComponent(smtp.server) + ':' + smtp.port;
});

<|endoftext|>