<commit_before>registerHandler("GET", "/lastExecution", function(out){

    try {
        persistenceEngine.initializeClass("Alert");
        var result = persistenceEngine.query("select max(timestamp) as lastExecution from Alert",null);
        out.write(new java.lang.String(result.get("object")).getBytes("utf-8"));
    } catch (e) {
        print(e);
    }
});

registerHandler("GET", "/getAlertsByGroup", function(out,pathParams,requestParams){

    try {
        persistenceEngine.initializeClass("Alert");
        var params = new Packages.java.util.HashMap();
        params.put("group", requestParams.getStringParameter("group",""));
        var results = persistenceEngine.query("select * from Alert where group = :group",params);
        out.write(new java.lang.String(results).getBytes("utf-8"));
    } catch (e) {
        print(e);
    }
});

registerHandler("GET", "/getAlerts", function(out,pathParams,requestParams){

    try {

        persistenceEngine.initializeClass("Alert");

        var params = new Packages.java.util.HashMap();
        var alertType = requestParams.getStringArrayParameter("alertType",null);

        var viewHistory = false;
        var cdvGroup = requestParams.getStringParameter("cdvGroup",null);
        var cdvName = requestParams.getStringParameter("cdvName",null);

        var isEmpty = function(s){
            return (s == null || s == "");

        }

        if( !isEmpty(cdvGroup) && !isEmpty(cdvName) ){
            viewHistory = true;
        }

        console.log("cdvGroup: " + (cdvName==null) + "; ViewHistory: " + viewHistory);

        // params.put("alertType", alertType);
        params.put("cdvGroup", cdvGroup);
        params.put("cdvName", cdvName);

        console.log("Starting getAlerts: " +  new Date() + "; " + alertType + "; >" + (viewHistory?("View History: " + cdvGroup + ':' + cdvName):"<"));

        var results;

        console.log("Alert filters: DISABLED FOR NOW UNTIL WE MAKE THIS WORK ");

        var where = "";
        if(viewHistory){
            // where = " and group = \""+cdvGroup+"\" and name = \""+cdvName+"\" ";
            console.log("Setting where");
            where = " and group = :cdvGroup and name = :cdvName ";

        }

        // TODO - Get this into prepared statements. Currently, seems orient doesn't properly support them
        if (alertType != null){
            var alertTypes = [], i = 0;
            for( i = 0; i < alertType.length; i++ ){
                var s = '"'+alertType[i] + '"';
                alertTypes.push(s);
                console.log("AlertType " + i + ": " + s);
            }

            if(alertTypes.length > 0){
                where += " and level in [ " + alertTypes.join(" , ") + "]";
            }

        }

        if(alertType && false){
            results = persistenceEngine.query("select timestamp, "+
                " group, name, message, userid, level, @rid as rid from Alert where level in [ :alertType ] " + where + " order by rid desc limit 100", params );

        }
        else{
            results = persistenceEngine.query("select timestamp, "+
                " group, name, message, userid, level, @rid as rid from Alert where 1 = 1 " + where + " order by rid desc limit 100",params);

        }

        //console.log("Results: " + results.toJSON());

        var object = JSON.parse(results.toString());
        console.log("Finished parsing: " +  new Date());

        _.map(object,function(l){

            // Add timestamp group
            l.timestampGroup = wd.cdv.utils.groupTimestamp(l.timestamp,2);
            return l;

        });

        console.log("Finishing getAlerts: " +  new Date());
        out.write(new java.lang.String(JSON.stringify(object,null,2)).getBytes("utf-8"));

    } catch (e) {
        print(e);
    }
});

registerHandler("GET", "/getLatestResults", function(out){

    try {

        persistenceEngine.initializeClass("TestResult");

        var results = persistenceEngine.query("select test.name as name, test.group as group, testResult.type as level, testResult.description as message" +
            " from TestResult where latest = true",null);

        var object = JSON.parse(results.getJSONArray("object").toString());

        out.write(new java.lang.String(JSON.stringify(object,null,2)).getBytes("utf-8"));

    } catch (e) {
        print(e);
    }
});

registerHandler("GET", "/getCdaErrors", function(out){

    try {

        persistenceEngine.initializeClass("cdaEvent");
        var results = persistenceEngine.query("select timestamp, "+
            "queryInfo.cdaSettingsId.append(\"[\").append(queryInfo.dataAccessId).append(\"]\") as file, queryInfo.parameters as parameters , "+
            "message, @rid from cdaEvent where eventType = 'QueryError'  order by timestamp desc limit 100",null);

        //console.log("Results: " + results.toJSON());

        var object = JSON.parse(results.getJSONArray("object").toString());
        _.map(object,function(l){

            // Add timestamp group
            l.timestampGroup = wd.cdv.utils.groupTimestamp(l.timestamp,2);

            // Put the files as we need them

            var params = _.flatten(l.parameters);
            var paramsArray = [];
            for(i = 0; i< params.length/2; i++){
                paramsArray.push([params[i*2],params[i*2+1]]);
            }

            if(paramsArray.length > 0){
                var paramStr = _.map(paramsArray,function(m){
                    return m.join(": ")
                }).join(", ");
                l.file = l.file + " (" + paramStr + ")";
            }
            delete l.parameters;
            return l;

        });

        out.write(new java.lang.String(JSON.stringify(object,null,2)).getBytes("utf-8"));

    } catch (e) {
        print(e);
    }
});

registerHandler("GET", "/getCdaSlowQueries", function(out){

    try {

        persistenceEngine.initializeClass("cdaEvent");
        var results = persistenceEngine.query("select timestamp, "+
            "queryInfo.cdaSettingsId.append(\"[\").append(queryInfo.dataAccessId).append(\"]\") as file, queryInfo.parameters as parameters , "+
            "duration, @rid from cdaEvent where eventType = 'QueryTooLong'  order by timestamp desc limit 100",null);

        //console.log("Results: " + results.toJSON());

        var object = JSON.parse(results.getJSONArray("object").toString());
        _.map(object,function(l){

            // Add timestamp group
            l.timestampGroup = wd.cdv.utils.groupTimestamp(l.timestamp,2);

            // Put the files as we need them

            var params = _.flatten(l.parameters);
            var paramsArray = [];
            for(i = 0; i< params.length/2; i++){
                paramsArray.push([params[i*2],params[i*2+1]]);
            }

            if(paramsArray.length > 0){
                var paramStr = _.map(paramsArray,function(m){
                    return m.join(": ")
                }).join(", ");
                l.file = l.file + " (" + paramStr + ")";
            }
            delete l.parameters;
            return l;

        });

        out.write(new java.lang.String(JSON.stringify(object,null,2)).getBytes("utf-8"));

    } catch (e) {
        print(e);
    }
});

registerHandler("GET", "/deleteCdaEntry", function(out,pathParams,requestParams){

    try {

        console.log("deleteCdaEntry method ");
        persistenceEngine.initializeClass("cdaEvent");
        var params = new Packages.java.util.HashMap();
        var cdaEntryId = requestParams.getStringParameter("cdaEntryId","")
        params.put("cdaEntryId", cdaEntryId);

        // 1. Find queryInfo.@rid
        var queryInfoRid = JSON.parse(persistenceEngine.query("select queryInfo from cdaEvent where  @rid = :cdaEntryId ", params).getJSONArray("object").toString())[0].queryInfo;
        console.log("Deleting queryInfoRid: " + queryInfoRid);
        persistenceEngine.deleteRecord(queryInfoRid);
        persistenceEngine.deleteRecord(cdaEntryId);

        out.write(new java.lang.String("{result: true}").getBytes("utf-8"));

    /*
        var results = persistenceEngine.query("delete from ( traverse * from (select from cdaEvent where  @rid = \"" + cdaEntryId + "\" ) where  $depth <= 1 )", params);
        out.write(new java.lang.String(results).getBytes("utf-8"));
         */

    } catch (e) {
        print(e);
    }
});

registerHandler("GET", "/deleteCdaEntriesOfEventType", function(out,pathParams,requestParams){

    try {

        persistenceEngine.initializeClass("cdaEvent");
        var params = new Packages.java.util.HashMap();
        var eventType = requestParams.getStringParameter("eventType","")
        params.put("eventType", eventType);

        console.log("Deleting eventTypes: " + eventType);

        // 1. Find queryInfo.@rid
        var result = persistenceEngine.command("delete from cdaEvent where eventType = :eventType ", params)
        out.write(new java.lang.String(result).getBytes("utf-8"));

    } catch (e) {
        print(e);
    }
});

registerHandler("GET", "/deleteAlert", function(out,pathParams,requestParams){

    try {

        console.log("deleteAlert method ");
        persistenceEngine.initializeClass("Alerts");
        var params = new Packages.java.util.HashMap();
        var cdaEntryId = requestParams.getStringParameter("alertId","")
        params.put("alertId", cdaEntryId);

        // 1. Delete it
        var result = persistenceEngine.command("delete from Alert where  @rid = :alertId ", params);
        out.write(new java.lang.String("{result: true}").getBytes("utf-8"));

    } catch (e) {
        print(e);
    }
});

registerHandler("GET", "/deleteAllAlerts", function(out,pathParams,requestParams){

    try {

        console.log("deleteAllAlerts method ");
        persistenceEngine.initializeClass("Alert");

        // 1. Truncate it
        var result = persistenceEngine.command("truncate class Alert ", null);
        out.write(new java.lang.String("{result: true}").getBytes("utf-8"));

    } catch (e) {
        print(e);
    }
});
<commit_msg>fix a bad merge<commit_after>registerHandler("GET", "/lastExecution", function(out){

    try {
        persistenceEngine.initializeClass("Alert");
        var result = persistenceEngine.query("select max(timestamp) as lastExecution from Alert",null);
        out.write(new java.lang.String(result.get("object")).getBytes("utf-8"));
    } catch (e) {
        print(e);
    }
});

registerHandler("GET", "/getAlertsByGroup", function(out,pathParams,requestParams){

    try {
        persistenceEngine.initializeClass("Alert");
        var params = new Packages.java.util.HashMap();
        params.put("group", requestParams.getStringParameter("group",""));
        var results = persistenceEngine.query("select * from Alert where group = :group",params);
        out.write(new java.lang.String(results).getBytes("utf-8"));
    } catch (e) {
        print(e);
    }
});

registerHandler("GET", "/getAlerts", function(out,pathParams,requestParams){

    try {

        persistenceEngine.initializeClass("Alert");

        var params = new Packages.java.util.HashMap();
        var alertType = requestParams.getStringArrayParameter("alertType",null);

        var viewHistory = false;
        var cdvGroup = requestParams.getStringParameter("cdvGroup",null);
        var cdvName = requestParams.getStringParameter("cdvName",null);

        var isEmpty = function(s){
            return (s == null || s == "");

        }

        if( !isEmpty(cdvGroup) && !isEmpty(cdvName) ){
            viewHistory = true;
        }

        console.log("cdvGroup: " + (cdvName==null) + "; ViewHistory: " + viewHistory);

        // params.put("alertType", alertType);
        params.put("cdvGroup", cdvGroup);
        params.put("cdvName", cdvName);

        console.log("Starting getAlerts: " +  new Date() + "; " + alertType + "; >" + (viewHistory?("View History: " + cdvGroup + ':' + cdvName):"<"));

        var results;

        console.log("Alert filters: DISABLED FOR NOW UNTIL WE MAKE THIS WORK ");

        var where = "";
        if(viewHistory){
            // where = " and group = \""+cdvGroup+"\" and name = \""+cdvName+"\" ";
            console.log("Setting where");
            where = " and group = :cdvGroup and name = :cdvName ";

        }

        // TODO - Get this into prepared statements. Currently, seems orient doesn't properly support them
        if (alertType != null){
            var alertTypes = [], i = 0;
            for( i = 0; i < alertType.length; i++ ){
                var s = '"'+alertType[i] + '"';
                alertTypes.push(s);
                console.log("AlertType " + i + ": " + s);
            }

            if(alertTypes.length > 0){
                where += " and level in [ " + alertTypes.join(" , ") + "]";
            }

        }

        if(alertType && false){
            results = persistenceEngine.query("select timestamp, "+
                " group, name, message, userid, level, @rid as rid from Alert where level in [ :alertType ] " + where + " order by rid desc limit 100", params );

        }
        else{
            results = persistenceEngine.query("select timestamp, "+
                " group, name, message, userid, level, @rid as rid from Alert where 1 = 1 " + where + " order by rid desc limit 100",params);

        }

        //console.log("Results: " + results.toJSON());

        var object = JSON.parse(results.getJSONArray("object").toString());

        _.map(object,function(l){

            // Add timestamp group
            l.timestampGroup = wd.cdv.utils.groupTimestamp(l.timestamp,2);
            return l;

        });

        console.log("Finishing getAlerts: " +  new Date());
        out.write(new java.lang.String(JSON.stringify(object,null,2)).getBytes("utf-8"));

    } catch (e) {
        print(e);
    }
});

registerHandler("GET", "/getLatestResults", function(out){

    try {

        persistenceEngine.initializeClass("TestResult");

        var results = persistenceEngine.query("select test.name as name, test.group as group, testResult.type as level, testResult.description as message" +
            " from TestResult where latest = true",null);

        var object = JSON.parse(results.getJSONArray("object").toString());

        out.write(new java.lang.String(JSON.stringify(object,null,2)).getBytes("utf-8"));

    } catch (e) {
        print(e);
    }
});

registerHandler("GET", "/getCdaErrors", function(out){

    try {

        persistenceEngine.initializeClass("cdaEvent");
        var results = persistenceEngine.query("select timestamp, "+
            "queryInfo.cdaSettingsId.append(\"[\").append(queryInfo.dataAccessId).append(\"]\") as file, queryInfo.parameters as parameters , "+
            "message, @rid from cdaEvent where eventType = 'QueryError'  order by timestamp desc limit 100",null);

        //console.log("Results: " + results.toJSON());

        var object = JSON.parse(results.getJSONArray("object").toString());
        _.map(object,function(l){

            // Add timestamp group
            l.timestampGroup = wd.cdv.utils.groupTimestamp(l.timestamp,2);

            // Put the files as we need them

            var params = _.flatten(l.parameters);
            var paramsArray = [];
            for(i = 0; i< params.length/2; i++){
                paramsArray.push([params[i*2],params[i*2+1]]);
            }

            if(paramsArray.length > 0){
                var paramStr = _.map(paramsArray,function(m){
                    return m.join(": ")
                }).join(", ");
                l.file = l.file + " (" + paramStr + ")";
            }
            delete l.parameters;
            return l;

        });

        out.write(new java.lang.String(JSON.stringify(object,null,2)).getBytes("utf-8"));

    } catch (e) {
        print(e);
    }
});

registerHandler("GET", "/getCdaSlowQueries", function(out){

    try {

        persistenceEngine.initializeClass("cdaEvent");
        var results = persistenceEngine.query("select timestamp, "+
            "queryInfo.cdaSettingsId.append(\"[\").append(queryInfo.dataAccessId).append(\"]\") as file, queryInfo.parameters as parameters , "+
            "duration, @rid from cdaEvent where eventType = 'QueryTooLong'  order by timestamp desc limit 100",null);

        //console.log("Results: " + results.toJSON());

        var object = JSON.parse(results.getJSONArray("object").toString());
        _.map(object,function(l){

            // Add timestamp group
            l.timestampGroup = wd.cdv.utils.groupTimestamp(l.timestamp,2);

            // Put the files as we need them

            var params = _.flatten(l.parameters);
            var paramsArray = [];
            for(i = 0; i< params.length/2; i++){
                paramsArray.push([params[i*2],params[i*2+1]]);
            }

            if(paramsArray.length > 0){
                var paramStr = _.map(paramsArray,function(m){
                    return m.join(": ")
                }).join(", ");
                l.file = l.file + " (" + paramStr + ")";
            }
            delete l.parameters;
            return l;

        });

        out.write(new java.lang.String(JSON.stringify(object,null,2)).getBytes("utf-8"));

    } catch (e) {
        print(e);
    }
});

registerHandler("GET", "/deleteCdaEntry", function(out,pathParams,requestParams){

    try {

        console.log("deleteCdaEntry method ");
        persistenceEngine.initializeClass("cdaEvent");
        var params = new Packages.java.util.HashMap();
        var cdaEntryId = requestParams.getStringParameter("cdaEntryId","")
        params.put("cdaEntryId", cdaEntryId);

        // 1. Find queryInfo.@rid
        var queryInfoRid = JSON.parse(persistenceEngine.query("select queryInfo from cdaEvent where  @rid = :cdaEntryId ", params).getJSONArray("object").toString())[0].queryInfo;
        console.log("Deleting queryInfoRid: " + queryInfoRid);
        persistenceEngine.deleteRecord(queryInfoRid);
        persistenceEngine.deleteRecord(cdaEntryId);

        out.write(new java.lang.String("{result: true}").getBytes("utf-8"));

    /*
        var results = persistenceEngine.query("delete from ( traverse * from (select from cdaEvent where  @rid = \"" + cdaEntryId + "\" ) where  $depth <= 1 )", params);
        out.write(new java.lang.String(results).getBytes("utf-8"));
         */

    } catch (e) {
        print(e);
    }
});

registerHandler("GET", "/deleteCdaEntriesOfEventType", function(out,pathParams,requestParams){

    try {

        persistenceEngine.initializeClass("cdaEvent");
        var params = new Packages.java.util.HashMap();
        var eventType = requestParams.getStringParameter("eventType","")
        params.put("eventType", eventType);

        console.log("Deleting eventTypes: " + eventType);

        // 1. Find queryInfo.@rid
        var result = persistenceEngine.command("delete from cdaEvent where eventType = :eventType ", params)
        out.write(new java.lang.String(result).getBytes("utf-8"));

    } catch (e) {
        print(e);
    }
});

registerHandler("GET", "/deleteAlert", function(out,pathParams,requestParams){

    try {

        console.log("deleteAlert method ");
        persistenceEngine.initializeClass("Alerts");
        var params = new Packages.java.util.HashMap();
        var cdaEntryId = requestParams.getStringParameter("alertId","")
        params.put("alertId", cdaEntryId);

        // 1. Delete it
        var result = persistenceEngine.command("delete from Alert where  @rid = :alertId ", params);
        out.write(new java.lang.String("{result: true}").getBytes("utf-8"));

    } catch (e) {
        print(e);
    }
});

registerHandler("GET", "/deleteAllAlerts", function(out,pathParams,requestParams){

    try {

        console.log("deleteAllAlerts method ");
        persistenceEngine.initializeClass("Alert");

        // 1. Truncate it
        var result = persistenceEngine.command("truncate class Alert ", null);
        out.write(new java.lang.String("{result: true}").getBytes("utf-8"));

    } catch (e) {
        print(e);
    }
});
<|endoftext|>


<commit_before>// Code below taken from http://eureka.ykyuen.info/2015/02/26/meteor-run-shell-command-at-server-side/

var spawn = Meteor.npmRequire('child_process').spawn;

var lineToResultObj = function(line) {
  var elts;
  var newObj;
  var line;

  elts = line.split('\t');

  newObj = {
    text: elts[2],
    corpus: elts[1]
  };

  if (newObj.text === undefined)
    return;

  return newObj;
};

// We use Streamy's s.id attribute to uniquely map from client to child process. This is to
// ensure that with more than one user, we will only ever kill that user's process.
var procs = {};
var limits = {};
var numsSent = {};
var lineBuffers = {};
Streamy.on('search', function(d, s) {
  var o;
  var lineBuffer = lineBuffers[s.id];
  var csearch = procs[s.id];

  console.dir(s);

  try {
  console.log("Server hears an emission on 'search'", d, "from client", s.id, "at", s.headers['x-forwarded-for']);
  }
  catch (e) {
    console.log("Server hears an emission on 'search'", d, "from client", s.id);
  }

  console.log(s.ip);

  switch (d.type) {
    case 'KILL':
      if (csearch) {
        csearch.kill('SIGKILL');
        delete procs[s.id];
      }
      break;

    case 'INCR':
      if (csearch && numsSent[s.id] >= limits[s.id] - d.amt) {
        limits[s.id] += d.amt;
        csearch.kill('SIGCONT');
      }
      else {
        throw new Meteor.Error(500, "Tried to INCR a csearch that didn't exist!");
      }
      break;

    // we have a new search--spawn a new process for it
    case 'TERM':
      csearch = spawn(Meteor.settings.CSEARCH_PATH, ['-n', d.term]);
      procs[s.id] = csearch;
      numsSent[s.id] = 0;
      limits[s.id] = 20;
      lineBuffer = [];
      lineBuffers[s.id] = lineBuffer;

      csearch.stdout.on('data', function (data) {
        //console.log(data.toString(), numsSent[s.id], "of", limits[s.id]);

        // NOTE: this could give us more than one line at a time, so we must split on \n
        var lines = data.toString().split('\n');
        lines = lines.slice(0, lines.length-1); //get rid of empty line at the end
        lines.forEach(function(line) {
          lineBuffer.push(line);
        });

        while (lineBuffer[numsSent[s.id]] && numsSent[s.id] < limits[s.id]) {
          o = lineToResultObj(lineBuffer[numsSent[s.id]]);
          if (o) {
            Streamy.emit('search', {
              type: 'DATA',
              data: o
            }, s);
            numsSent[s.id]++;
          } else {
            lineBuffer = lineBuffer.splice(numSent, 1);
          }
        }

        if (numsSent[s.id] >= limits[s.id]) {
          csearch.kill('SIGSTOP');
        }

      });

      // let client know when we're done
      csearch.on('close', function() {
        Streamy.emit('search', {type: 'DONE'}, s);
      });
      break;

    default:
      throw new Meteor.Error(500, "Malformed Streamy emission received on server--how did that happen?");
  }
});

/* INDEX UPDATE */
// This code calls cindex every hour to check for changes to local files.
var callCindex = function() {
  console.log("Running cindex cron job");
  spawn(Meteor.settings.CINDEX_PATH); // no args--we already know _what_ to index
};

var cron = new Meteor.Cron({
  events: {
    "0 * * * *": callCindex
  }
});
<commit_msg>removed junk logs<commit_after>// Code below taken from http://eureka.ykyuen.info/2015/02/26/meteor-run-shell-command-at-server-side/

var spawn = Meteor.npmRequire('child_process').spawn;

var lineToResultObj = function(line) {
  var elts;
  var newObj;
  var line;

  elts = line.split('\t');

  newObj = {
    text: elts[2],
    corpus: elts[1]
  };

  if (newObj.text === undefined)
    return;

  return newObj;
};

// We use Streamy's s.id attribute to uniquely map from client to child process. This is to
// ensure that with more than one user, we will only ever kill that user's process.
var procs = {};
var limits = {};
var numsSent = {};
var lineBuffers = {};
Streamy.on('search', function(d, s) {
  var o;
  var lineBuffer = lineBuffers[s.id];
  var csearch = procs[s.id];

  try {
    console.log("Server hears an emission on 'search'", d, "from client", s.id, "at", s.headers['x-forwarded-for']);
  }
  catch (e) {
    console.log("Server hears an emission on 'search'", d, "from client", s.id);
  }

  switch (d.type) {
    case 'KILL':
      if (csearch) {
        csearch.kill('SIGKILL');
        delete procs[s.id];
      }
      break;

    case 'INCR':
      if (csearch && numsSent[s.id] >= limits[s.id] - d.amt) {
        limits[s.id] += d.amt;
        csearch.kill('SIGCONT');
      }
      else {
        throw new Meteor.Error(500, "Tried to INCR a csearch that didn't exist!");
      }
      break;

    // we have a new search--spawn a new process for it
    case 'TERM':
      csearch = spawn(Meteor.settings.CSEARCH_PATH, ['-n', d.term]);
      procs[s.id] = csearch;
      numsSent[s.id] = 0;
      limits[s.id] = 20;
      lineBuffer = [];
      lineBuffers[s.id] = lineBuffer;

      csearch.stdout.on('data', function (data) {
        //console.log(data.toString(), numsSent[s.id], "of", limits[s.id]);

        // NOTE: this could give us more than one line at a time, so we must split on \n
        var lines = data.toString().split('\n');
        lines = lines.slice(0, lines.length-1); //get rid of empty line at the end
        lines.forEach(function(line) {
          lineBuffer.push(line);
        });

        while (lineBuffer[numsSent[s.id]] && numsSent[s.id] < limits[s.id]) {
          o = lineToResultObj(lineBuffer[numsSent[s.id]]);
          if (o) {
            Streamy.emit('search', {
              type: 'DATA',
              data: o
            }, s);
            numsSent[s.id]++;
          } else {
            lineBuffer = lineBuffer.splice(numSent, 1);
          }
        }

        if (numsSent[s.id] >= limits[s.id]) {
          csearch.kill('SIGSTOP');
        }

      });

      // let client know when we're done
      csearch.on('close', function() {
        Streamy.emit('search', {type: 'DONE'}, s);
      });
      break;

    default:
      throw new Meteor.Error(500, "Malformed Streamy emission received on server--how did that happen?");
  }
});

/* INDEX UPDATE */
// This code calls cindex every hour to check for changes to local files.
var callCindex = function() {
  console.log("Running cindex cron job");
  spawn(Meteor.settings.CINDEX_PATH); // no args--we already know _what_ to index
};

var cron = new Meteor.Cron({
  events: {
    "0 * * * *": callCindex
  }
});
<|endoftext|>


<commit_before>'use strict';

var userRepository = require('../components/repositories/users'),
    Bluebird = require('bluebird');

module.exports = {

    listUsers (req, res, next) {
        console.log('listing users [' + req.path + ']');
        console.log('query:' + JSON.stringify(req.query, null, 2));

        userRepository.getUsers().then((users) => {
            req.entity = users;
            next();
        }).catch((err) => {
            next(err);
        });
    },

    listUsersPermission (req, res, next) {
        console.log('looking up repo permissions for users');

        let repoId = req.query.permission_repo,
            users = req.entity;

        if (repoId) {
            userRepository.getPermissions(repoId, users).then((users) => {
                req.entity = users;
                next();
            }).catch((err) => {
                next(err);
            });
        } else {
            next();
        }
    },

    listUsersLinks (req, res, next) {
        console.log('checking for links on user list');

        let repo = req.query.permission_repo,
            users = req.entity;

        //TODO: of course, these links should only appear if logged in user has admin on the repo also
        if (repo) {
            users.forEach((user) => {
                user.links = [{
                    rel: 'edit-repo-permission',
                    href: 'users/' + user.username + '/repos/' + repo + '/permissions/{permission}',
                    method: 'PUT'
                }, {
                    rel: 'remove-repo-permission',
                    href: 'users/' + user.username + '/repos/' + repo,
                    method: 'DELETE'
                }];
            });
        }
        next();
    },

    readUser (req, res, next) {
        console.log('getting user [' + req.path + ']');
        console.log('params:' + JSON.stringify(req.params, null, 2));

        let username = req.params.username;
        userRepository.getUser(username).then((user) => {
            req.entity = user;
            next();
        }).catch((err) => {
            next(err);
        });
    }
};
<commit_msg>#20 - checking for admin status before adding Edit/Delete repo links<commit_after>'use strict';

var userRepository = require('../components/repositories/users'),
    Bluebird = require('bluebird');

module.exports = {

    listUsers (req, res, next) {
        console.log('listing users [' + req.path + ']');
        console.log('query:' + JSON.stringify(req.query, null, 2));

        userRepository.getUsers().then((users) => {
            req.entity = users;
            next();
        }).catch((err) => {
            next(err);
        });
    },

    listUsersPermission (req, res, next) {
        console.log('looking up repo permissions for users');

        let repoId = req.query.permission_repo,
            users = req.entity;

        if (repoId) {
            userRepository.getPermissions(repoId, users).then((users) => {
                req.entity = users;
                next();
            }).catch((err) => {
                next(err);
            });
        } else {
            next();
        }
    },

    listUsersLinks (req, res, next) {
        console.log('checking for links on user list');

        let repo = req.query.permission_repo,
            users = req.entity;

        if (repo) {
            users.forEach((user) => {
                let permission = user.permission;
                if (permission.permissive === 'admin' || permission.github === 'admin') {
                    user.links = [{
                        rel: 'edit-repo-permission',
                        href: 'users/' + user.username + '/repos/' + repo + '/permissions/{permission}',
                        method: 'PUT'
                    }, {
                        rel: 'remove-repo-permission',
                        href: 'users/' + user.username + '/repos/' + repo,
                        method: 'DELETE'
                    }];
                }
            });
        }
        next();
    },

    readUser (req, res, next) {
        console.log('getting user [' + req.path + ']');
        console.log('params:' + JSON.stringify(req.params, null, 2));

        let username = req.params.username;
        userRepository.getUser(username).then((user) => {
            req.entity = user;
            next();
        }).catch((err) => {
            next(err);
        });
    }
};
<|endoftext|>


<commit_before>// set undefined env vars
if (!process.env.MAPBOX_API) require('../../../config/env')

export const name = 'search'

export const MAPBOX_ACCESS_TOKEN = process.env.MAPBOX_API

export const GEOCODE_OPTIONS = {
  types: 'region,postcode,place,locality,neighborhood,address',
  country: 'gb',
  // weight results closer to London
  proximity: { latitude: 51.528308, longitude: -0.3817765 },
  // set bounding box to Greater London
  bbox: [ -0.489, 51.28, 0.236, 51.686 ]
}
<commit_msg>fix London center coords<commit_after>// set undefined env vars
if (!process.env.MAPBOX_API) require('../../../config/env')

export const name = 'search'

export const MAPBOX_ACCESS_TOKEN = process.env.MAPBOX_API

export const GEOCODE_OPTIONS = {
  types: 'region,postcode,place,locality,neighborhood,address',
  country: 'gb',
  // weight results closer to London
  proximity: { latitude: 51.507463, longitude: -0.127694 },
  // set bounding box to Greater London
  bbox: [ -0.489, 51.28, 0.236, 51.686 ]
}
<|endoftext|>


<commit_before>const { dialog } = require('electron').remote
const fs = require('fs')
const bubbleTxt = require('../helpers/bubbleTxt.js')

let threatsArray = []
let constraintsArray = []
let mechanismsArray = []
let vulnerabilityArray = []
/**
 * generate security report
 *
 * @param {Object} cy cytoscape instance
 */
module.exports = function generateReport (cy) {
  cy.nodes().map(node => {
    if (node.data().asto.concept === 'threat') {
      threatsArray.push(node)
    }
    if (node.data().asto.concept === 'constraint') {
      constraintsArray.push(node)
    }
  })

  const reportTittle = `# Security Report\n\n`
  const numberOfThreats = `Number of threats: ${threatsArray.length}\n`
  const numberOfTConstraints = `Number of constraints: ${
    constraintsArray.length
  }\n`
  const numberOfMechanisms = `Number of threats: ${mechanismsArray.length}\n`
  const numberOfVulnerabilities = `Number of threats: ${
    vulnerabilityArray.length
  }\n`

  const dataToWrite = reportTittle
    .concat(numberOfThreats)
    .concat(numberOfTConstraints)
    .concat(numberOfMechanisms)
    .concat(numberOfVulnerabilities)

  let dialogOptions = []
  process.platform === 'darwin'
    ? (dialogOptions = ['openFile', 'openDirectory'])
    : (dialogOptions = ['openFile'])
  dialog.showSaveDialog(
    {
      properties: [...dialogOptions],
      filters: [{ name: 'markdown', extensions: ['md'] }]
    },
    filename => {
      fs.writeFile(`${filename}`, dataToWrite, err => {
        if (err) {
          dialog.showErrorBox('There was an error while saving the file', err.message)
        }
        bubbleTxt('security report generated\n👍')
      })
    }
  )
}
<commit_msg>show errors in dialog<commit_after>const { dialog } = require('electron').remote
const fs = require('fs')
const bubbleTxt = require('../helpers/bubbleTxt.js')

let threatsArray = []
let constraintsArray = []
let mechanismsArray = []
let vulnerabilityArray = []
/**
 * generate security report
 *
 * @param {Object} cy cytoscape instance
 */
module.exports = function generateReport (cy) {
  cy.nodes().map(node => {
    if (node.data().asto.concept === 'threat') {
      threatsArray.push(node)
    }
    if (node.data().asto.concept === 'constraint') {
      constraintsArray.push(node)
    }
  })

  const reportTittle = `# Security Report\n\n`
  const numberOfThreats = `Number of threats: ${threatsArray.length}\n`
  const numberOfTConstraints = `Number of constraints: ${
    constraintsArray.length
  }\n`
  const numberOfMechanisms = `Number of threats: ${mechanismsArray.length}\n`
  const numberOfVulnerabilities = `Number of threats: ${
    vulnerabilityArray.length
  }\n`

  const dataToWrite = reportTittle
    .concat(numberOfThreats)
    .concat(numberOfTConstraints)
    .concat(numberOfMechanisms)
    .concat(numberOfVulnerabilities)

  let dialogOptions = []
  process.platform === 'darwin'
    ? (dialogOptions = ['openFile', 'openDirectory'])
    : (dialogOptions = ['openFile'])
  dialog.showSaveDialog(
    {
      properties: [...dialogOptions],
      filters: [{ name: 'markdown', extensions: ['md'] }]
    },
    filename => {
      fs.writeFile(`${filename}`, dataToWrite, err => {
        if (err) {
          dialog.showErrorBox(
            'There was an error while saving the file',
            err.message
          )
        }
        bubbleTxt('security report generated\n👍')
      })
    }
  )
}
<|endoftext|>


<commit_before>/*
 * Pines Common JavaScript
 * http://sciactive.com/
 *
 * Copyright 2010 SciActive.com
 * Hunter Perrin <hunter@sciactive.com>
 *
 * Licensed GNU AGPL
 * http://www.gnu.org/licenses/agpl-3.0.html
 */
// Make sure arrays have an indexOf method.
if (!Array.prototype.indexOf) {
	Array.prototype.indexOf = function(elt /*, from*/) {
		var len = this.length >>> 0;
		var from = Number(arguments[1]) || 0;
		from = (from < 0) ? Math.ceil(from) : Math.floor(from);
		if (from < 0)
			from += len;
		for (; from < len; from++) {
			if (from in this &&
				this[from] === elt)
			return from;
		}
		return -1;
	};
}

// If pines doesn't exist, make it.
if (!window.pines) {
(function(window, document){

// Shortcut to pines.ready.
var pines = function(fn){pines.ready(fn)};

pines.full_location="";
pines.rela_location="";
// Cause the browser to go to a URL. (Just like the user clicked a link.)
pines.get=function(url, params){
	if (params) {
		url += (url.indexOf("?") == -1) ? "?" : "&";
		var parray = [];
		for (var i in params) {
			if (params.hasOwnProperty(i)) {
				if (encodeURIComponent)
					parray.push(encodeURIComponent(i)+"="+encodeURIComponent(params[i]));
				else
					parray.push(escape(i)+"="+escape(params[i]));
			}
		}
		url += parray.join("&");
	}
	window.location = url;
};
// Cause the browser to POST data. (Just like the user submitted a form.)
pines.post=function(url, params){
	var form = document.createElement("form");
	form.action = url;
	form.method = "POST";
	for (var i in params) {
		if (params.hasOwnProperty(i)) {
			var input = document.createElement("input");
			input.type = "hidden";
			input.name = i;
			input.value = params[i];
			form.appendChild(input);
		}
	}
	document.body.appendChild(form);
	form.submit();
};
// Executes a function after all JS files are loaded and the DOM is ready.
pines.ready=function(fn){
	_rdpending.push(fn);
	// If the DOM is loaded, run right now.
	if (_domloaded)
		_ready();
};
// Executes a function in turn with the loading JS files.
pines.load=function(fn){
	_pendingjs.push(fn);
	if (_pendingjs.length==1)
		_loadnext();
};
// Loads JavaScript files in turn.
pines.loadjs=function(url, multiple){
	if (_loadedjs.indexOf(url) > -1 && !multiple) return;
	// Mark that the JS is loading.
	_jscloaded = false;
	var n=document.createElement("script");
	n.setAttribute("type","text/javascript");
	n.setAttribute("src",url);
	_pendingjs.push(n);
	if (_pendingjs.length==1)
		_loadnext();
	_loadedjs[_loadedjs.length]=url;
};
// Loads CSS files immediately.
pines.loadcss=function(url, multiple){
	if (_loadedcss.indexOf(url) > -1 && !multiple) return;
	var n=document.createElement("link");
	n.setAttribute("type","text/css");
	n.setAttribute("rel","stylesheet");
	n.setAttribute("href",url);
	if (typeof n!="undefined")
		document.getElementsByTagName("head")[0].appendChild(n);
	_loadedcss[_loadedcss.length]=url;
};
// Notify the user.
pines.alert=function(message, title){
	alert((title ? title : "Alert") + "\n\n" + message);
};
// Show the user an error.
pines.error=function(message, title){
	alert((title ? title : "Error") + "\n\n" + message);
};

// List of loaded JS files.
var _loadedjs=[],
// Loaded CSS files.
_loadedcss=[],
// Pending JS files.
_pendingjs=[],
// Pending ready functions.
_rdpending=[],
// Whether the DOM is ready.
_domloaded=false,
// Whether all JS/CSS files are ready.
_jscloaded=false,
_ready=function(){
	if ((_pendingjs && !_jscloaded) || !_domloaded) return;
	if (_rdpending) {
		var fn = _rdpending[0];
		while (fn) {
			_rdpending = _rdpending.slice(1);
			fn.call();
			fn = _rdpending[0];
		}
	}
	_rdpending = [];
},
// Load the next JS/CSS file.
_loadnext=function(){
	var n = _pendingjs[0];
	if (typeof n == "undefined") {
		_jscloaded = true;
		_ready();
		return;
	}
	if (typeof n == "function"){
		n.call();
		_pendingjs = _pendingjs.slice(1);
		_loadnext();
	} else {
		if (typeof n.readyState != "undefined"){ // IE
			n.onreadystatechange = function(){
				if (n.readyState=="loaded" || n.readyState=="complete"){
					n.onreadystatechange = null;
					_pendingjs = _pendingjs.slice(1);
					_loadnext();
				}
			};
		} else { // Others
			n.onload = function(){
				_pendingjs = _pendingjs.slice(1);
				_loadnext();
			};
		}
		if (typeof n!="undefined")
			document.getElementsByTagName("head")[0].appendChild(n);
	}
};

var dom_loaded;
// Cleanup functions for the document ready method
if ( document.addEventListener ) {
	dom_loaded = function(){
		document.removeEventListener("DOMContentLoaded", dom_loaded, false);
		_domloaded = true;
		_ready();
	};

} else if ( document.attachEvent ) {
	dom_loaded = function(){
		// Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
		if (document.readyState === "complete") {
			document.detachEvent("onreadystatechange", dom_loaded);
			_domloaded = true;
			_ready();
		}
	};
}

// The DOM ready check for Internet Explorer
var scroll_check = function(){
	if (_domloaded)
		return;
	try {
		// If IE is used, use the trick by Diego Perini
		// http://javascript.nwbox.com/IEContentLoaded/
		document.documentElement.doScroll("left");
	} catch(e) {
		setTimeout( scroll_check, 1 );
		return;
	}

	// and execute any waiting functions
	_ready();
}

if (document.attachEvent) { // IE
	// ensure firing before onload,
	// maybe late but safe also for iframes
	document.attachEvent("onreadystatechange", dom_loaded);
	// A fallback to window.onload, that will always work
	window.attachEvent("onload", function(){_domloaded = true; _ready();});
	// If IE and not a frame
	// continually check to see if the document is ready
	var toplevel = false;
	try {
		toplevel = window.frameElement == null;
	} catch(e) {}
	if (document.documentElement.doScroll && toplevel)
		doScrollCheck();
} else if (document.addEventListener) { // Others
	document.addEventListener("DOMContentLoaded", dom_loaded, false);
	window.addEventListener("load", function(){_domloaded = true; _ready();}, false);
}

window.pines = pines;

})(window, window.document);
}<commit_msg>Renamed JS pines.alert to pines.notice to more closely match the PHP function.<commit_after>/*
 * Pines Common JavaScript
 * http://sciactive.com/
 *
 * Copyright 2010 SciActive.com
 * Hunter Perrin <hunter@sciactive.com>
 *
 * Licensed GNU AGPL
 * http://www.gnu.org/licenses/agpl-3.0.html
 */
// Make sure arrays have an indexOf method.
if (!Array.prototype.indexOf) {
	Array.prototype.indexOf = function(elt /*, from*/) {
		var len = this.length >>> 0;
		var from = Number(arguments[1]) || 0;
		from = (from < 0) ? Math.ceil(from) : Math.floor(from);
		if (from < 0)
			from += len;
		for (; from < len; from++) {
			if (from in this &&
				this[from] === elt)
			return from;
		}
		return -1;
	};
}

// If pines doesn't exist, make it.
if (!window.pines) {
(function(window, document){

// Shortcut to pines.ready.
var pines = function(fn){pines.ready(fn)};

pines.full_location="";
pines.rela_location="";
// Cause the browser to go to a URL. (Just like the user clicked a link.)
pines.get=function(url, params){
	if (params) {
		url += (url.indexOf("?") == -1) ? "?" : "&";
		var parray = [];
		for (var i in params) {
			if (params.hasOwnProperty(i)) {
				if (encodeURIComponent)
					parray.push(encodeURIComponent(i)+"="+encodeURIComponent(params[i]));
				else
					parray.push(escape(i)+"="+escape(params[i]));
			}
		}
		url += parray.join("&");
	}
	window.location = url;
};
// Cause the browser to POST data. (Just like the user submitted a form.)
pines.post=function(url, params){
	var form = document.createElement("form");
	form.action = url;
	form.method = "POST";
	for (var i in params) {
		if (params.hasOwnProperty(i)) {
			var input = document.createElement("input");
			input.type = "hidden";
			input.name = i;
			input.value = params[i];
			form.appendChild(input);
		}
	}
	document.body.appendChild(form);
	form.submit();
};
// Executes a function after all JS files are loaded and the DOM is ready.
pines.ready=function(fn){
	_rdpending.push(fn);
	// If the DOM is loaded, run right now.
	if (_domloaded)
		_ready();
};
// Executes a function in turn with the loading JS files.
pines.load=function(fn){
	_pendingjs.push(fn);
	if (_pendingjs.length==1)
		_loadnext();
};
// Loads JavaScript files in turn.
pines.loadjs=function(url, multiple){
	if (_loadedjs.indexOf(url) > -1 && !multiple) return;
	// Mark that the JS is loading.
	_jscloaded = false;
	var n=document.createElement("script");
	n.setAttribute("type","text/javascript");
	n.setAttribute("src",url);
	_pendingjs.push(n);
	if (_pendingjs.length==1)
		_loadnext();
	_loadedjs[_loadedjs.length]=url;
};
// Loads CSS files immediately.
pines.loadcss=function(url, multiple){
	if (_loadedcss.indexOf(url) > -1 && !multiple) return;
	var n=document.createElement("link");
	n.setAttribute("type","text/css");
	n.setAttribute("rel","stylesheet");
	n.setAttribute("href",url);
	if (typeof n!="undefined")
		document.getElementsByTagName("head")[0].appendChild(n);
	_loadedcss[_loadedcss.length]=url;
};
// Notify the user.
pines.notice=function(message, title){
	alert((title ? title : "Notice") + "\n\n" + message);
};
// Show the user an error.
pines.error=function(message, title){
	alert((title ? title : "Error") + "\n\n" + message);
};

// List of loaded JS files.
var _loadedjs=[],
// Loaded CSS files.
_loadedcss=[],
// Pending JS files.
_pendingjs=[],
// Pending ready functions.
_rdpending=[],
// Whether the DOM is ready.
_domloaded=false,
// Whether all JS/CSS files are ready.
_jscloaded=false,
_ready=function(){
	if ((_pendingjs && !_jscloaded) || !_domloaded) return;
	if (_rdpending) {
		var fn = _rdpending[0];
		while (fn) {
			_rdpending = _rdpending.slice(1);
			fn.call();
			fn = _rdpending[0];
		}
	}
	_rdpending = [];
},
// Load the next JS/CSS file.
_loadnext=function(){
	var n = _pendingjs[0];
	if (typeof n == "undefined") {
		_jscloaded = true;
		_ready();
		return;
	}
	if (typeof n == "function"){
		n.call();
		_pendingjs = _pendingjs.slice(1);
		_loadnext();
	} else {
		if (typeof n.readyState != "undefined"){ // IE
			n.onreadystatechange = function(){
				if (n.readyState=="loaded" || n.readyState=="complete"){
					n.onreadystatechange = null;
					_pendingjs = _pendingjs.slice(1);
					_loadnext();
				}
			};
		} else { // Others
			n.onload = function(){
				_pendingjs = _pendingjs.slice(1);
				_loadnext();
			};
		}
		if (typeof n!="undefined")
			document.getElementsByTagName("head")[0].appendChild(n);
	}
};

var dom_loaded;
// Cleanup functions for the document ready method
if ( document.addEventListener ) {
	dom_loaded = function(){
		document.removeEventListener("DOMContentLoaded", dom_loaded, false);
		_domloaded = true;
		_ready();
	};

} else if ( document.attachEvent ) {
	dom_loaded = function(){
		// Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
		if (document.readyState === "complete") {
			document.detachEvent("onreadystatechange", dom_loaded);
			_domloaded = true;
			_ready();
		}
	};
}

// The DOM ready check for Internet Explorer
var scroll_check = function(){
	if (_domloaded)
		return;
	try {
		// If IE is used, use the trick by Diego Perini
		// http://javascript.nwbox.com/IEContentLoaded/
		document.documentElement.doScroll("left");
	} catch(e) {
		setTimeout( scroll_check, 1 );
		return;
	}

	// and execute any waiting functions
	_ready();
}

if (document.attachEvent) { // IE
	// ensure firing before onload,
	// maybe late but safe also for iframes
	document.attachEvent("onreadystatechange", dom_loaded);
	// A fallback to window.onload, that will always work
	window.attachEvent("onload", function(){_domloaded = true; _ready();});
	// If IE and not a frame
	// continually check to see if the document is ready
	var toplevel = false;
	try {
		toplevel = window.frameElement == null;
	} catch(e) {}
	if (document.documentElement.doScroll && toplevel)
		doScrollCheck();
} else if (document.addEventListener) { // Others
	document.addEventListener("DOMContentLoaded", dom_loaded, false);
	window.addEventListener("load", function(){_domloaded = true; _ready();}, false);
}

window.pines = pines;

})(window, window.document);
}<|endoftext|>


<commit_before>import _ from 'lodash';
import validator from 'validator'
import pgFormat from 'pg-format';

import { User, Group, Comment } from '../../models';
import { initObject, prepareModelPayload } from './utils';

const usersTrait = (superClass) => class extends superClass {
  async createUser(payload) {
    const preparedPayload = prepareModelPayload(payload, USER_COLUMNS, USER_COLUMNS_MAPPING)
    const [{ uid: uid, id: intId }] = await this.database('users').returning(['uid', 'id']).insert(preparedPayload);
    await this.createUserStats(uid)
    return [uid, intId];
  }

  updateUser(userId, payload) {
    const tokenExpirationTime = new Date(Date.now())
    const expireAfter = 60 * 60 * 24 // 24 hours

    const preparedPayload = prepareModelPayload(payload, USER_COLUMNS, USER_COLUMNS_MAPPING)

    if (_.has(preparedPayload, 'reset_password_token')) {
      tokenExpirationTime.setHours(tokenExpirationTime.getHours() + expireAfter)
      preparedPayload['reset_password_expires_at'] = tokenExpirationTime.toISOString()
    }

    this.cacheFlushUser(userId)
    return this.database('users').where('uid', userId).update(preparedPayload)
  }

  setUpdatedAtInGroupsByIds = async (groupIds, time) => {
    const updatedAt = new Date();
    updatedAt.setTime(time);

    const sql = pgFormat(`UPDATE "users" SET "updated_at" = ? WHERE "uid" IN (%L) AND "type"='group' RETURNING "uid"`, groupIds);
    const uids = await this.database.raw(sql, [updatedAt.toISOString()]);

    const flushPromises = uids.rows.map((row) => this.cacheFlushUser(row.uid));
    await Promise.all(flushPromises);
  };

  async existsUser(userId) {
    const res = await this.database('users').where('uid', userId).count()
    return parseInt(res[0].count)
  }

  async existsUsername(username) {
    const res = await this.database('users').where('username', username).count()
    return parseInt(res[0].count)
  }

  async existsUserEmail(email) {
    const res = await this.database('users').whereRaw('LOWER(email)=LOWER(?)', email).count()
    return parseInt(res[0].count)
  }

  async getUserById(id) {
    const user = await this.getFeedOwnerById(id)

    if (!user) {
      return null
    }

    if (!(user instanceof User)) {
      throw new Error(`Expected User, got ${user.constructor.name}`)
    }

    return user
  }

  async getUsersByIds(userIds) {
    const users = await this.getFeedOwnersByIds(userIds)

    _.each(users, (user) => {
      if (!(user instanceof User)) {
        throw new Error(`Expected User, got ${user.constructor.name}`)
      }
    })

    return users
  }

  async getUserByUsername(username) {
    const feed = await this.getFeedOwnerByUsername(username)

    if (null === feed) {
      return null
    }

    if (!(feed instanceof User)) {
      throw new Error(`Expected User, got ${feed.constructor.name}`)
    }

    return feed
  }

  async getUserByResetToken(token) {
    const attrs = await this.database('users').first().where('reset_password_token', token)

    if (!attrs) {
      return null
    }

    if (attrs.type !== 'user') {
      throw new Error(`Expected User, got ${attrs.type}`)
    }

    const now = new Date().getTime()
    if (attrs.reset_password_expires_at < now) {
      return null
    }

    return initUserObject(attrs);
  }

  async getUserByEmail(email) {
    const attrs = await this.database('users').first().whereRaw('LOWER(email)=LOWER(?)', email)

    if (!attrs) {
      return null
    }

    if (attrs.type !== 'user') {
      throw new Error(`Expected User, got ${attrs.type}`)
    }

    return initUserObject(attrs);
  }

  async _getUserIntIdByUUID(userUUID) {
    if (!validator.isUUID(userUUID, 4)) {
      return null;
    }

    const res = await this.database('users').returning('id').first().where('uid', userUUID);
    if (!res) {
      return null;
    }
    return res.id;
  }

  async getFeedOwnerById(id) {
    if (!validator.isUUID(id, 4)) {
      return null
    }
    return initUserObject(await this.fetchUser(id));
  }

  async getFeedOwnersByIds(ids) {
    return (await this.fetchUsers(ids)).map(initUserObject);
  }

  async getUsersByIdsAssoc(ids) {
    return _.mapValues(await this.fetchUsersAssoc(ids), initUserObject);
  }

  getUsersIdsByIntIds(intIds) {
    return this.database('users').select('id', 'uid').whereIn('id', intIds);
  }

  async getFeedOwnerByUsername(username) {
    const attrs = await this.database('users').first().where('username', username.toLowerCase())
    return initUserObject(attrs);
  }

  async getFeedOwnersByUsernames(usernames) {
    usernames = usernames.map((u) => u.toLowerCase());
    const users = await this.database('users').whereIn('username', usernames);
    return users.map(initUserObject);
  }

  async getGroupById(id) {
    const user = await this.getFeedOwnerById(id)

    if (!user) {
      return null
    }

    if (!(user instanceof Group)) {
      throw new Error(`Expected Group, got ${user.constructor.name}`)
    }

    return user
  }

  async getGroupByUsername(username) {
    const feed = await this.getFeedOwnerByUsername(username)

    if (null === feed) {
      return null
    }

    if (!(feed instanceof Group)) {
      throw new Error(`Expected Group, got ${feed.constructor.name}`)
    }

    return feed
  }

  async getUserSubscribersIds(userId) {
    return await this.database
      .pluck('s.user_id')
      .from('subscriptions as s')
      .innerJoin('feeds as f', 'f.uid', 's.feed_id')
      .where('f.name', 'Posts')
      .where('f.user_id', userId)
      .orderBy('s.created_at', 'desc');
  }

  // Insert record to 'archives' table for the test purposes.
  // 'params' should hold optional 'archives' fields.
  async setUserArchiveParams(userId, oldUsername, params = {}) {
    return await this.database('archives').insert({ ...params, user_id: userId, old_username: oldUsername });
  }

  // Return data from 'archives' table for the 'whoami' response
  async getUserArchiveParams(userId) {
    const params = await this.database('archives')
      .first('old_username', 'has_archive', 'via_sources', 'recovery_status', 'restore_comments_and_likes')
      .where({ user_id: userId });
    if (!params) {
      return null;
    }
    params.hidden_comments_count = 0;
    if (!params.restore_comments_and_likes) {
      const sql = `select count(*) from
        hidden_comments h
        join comments c on c.uid = h.comment_id
        where c.hide_type = :hideType and (h.user_id = :userId or h.old_username = :oldUsername)`;
      const res = await this.database.raw(sql, { hideType: Comment.HIDDEN_ARCHIVED, userId, oldUsername: params.old_username });
      params.hidden_comments_count = parseInt(res.rows[0].count);
    }
    return params;
  }

  async startArchiveRestoration(userId, params = {}) {
    params = {
      disable_comments: false,
      via_restore:      [],
      ...params,
      recovery_status:  1,
    };
    await this.database('archives').where('user_id', userId).update(params);
  }

  async enableArchivedActivitiesRestoration(userId) {
    await this.database('archives').where('user_id', userId).update({ restore_comments_and_likes: true });
  }

  async someUsersArePublic(userIds, anonymousFriendly) {
    if (userIds.length === 0) {
      return false;
    }
    const { rows } = await this.database.raw(
      `select 1 from users
      where
        not is_private
        and not (:anonymousFriendly and is_protected)
        and uid = any(:userIds)
      limit 1
      `,
      { userIds, anonymousFriendly }
    );
    return rows.length > 0;
  }

  /**
   * Returns UIDs of users who cah see any of
   * the given feeds. It is assumed that feeds
   * are private 'Posts' or 'Directs' feeds.
   *
   * @param {number[]} feedIntIds
   * @return {string[]}
   */
  async getUsersWhoCanSeePrivateFeeds(feedIntIds) {
    const { rows } = await this.database.raw(
      `
      -- Feed owners always can see these feeds
      select user_id from feeds where id = any(:feedIntIds)
      union
      -- Users who subscribed to feeds
      select s.user_id from
        subscriptions s
        join feeds f on f.uid = s.feed_id
      where f.id = any(:feedIntIds)
      `,
      { feedIntIds }
    );
    return _.uniq(_.map(rows, 'user_id'));
  }

  async getNotificationsDigestRecipients() {
    const users = await this.database('users')
      .where('type', 'user')
      .whereRaw(`preferences -> 'sendNotificationsDigest' = 'true'::jsonb`);
    return users.map(initUserObject);
  }
};

export default usersTrait;

///////////////////////////////////////////////////

export function initUserObject(attrs) {
  if (!attrs) {
    return null;
  }
  attrs = prepareModelPayload(attrs, USER_FIELDS, USER_FIELDS_MAPPING);
  return initObject(attrs.type === 'group' ? Group : User, attrs, attrs.id);
}

const USER_COLUMNS = {
  username:               'username',
  screenName:             'screen_name',
  email:                  'email',
  description:            'description',
  type:                   'type',
  profilePictureUuid:     'profile_picture_uuid',
  createdAt:              'created_at',
  updatedAt:              'updated_at',
  directsReadAt:          'directs_read_at',
  isPrivate:              'is_private',
  isProtected:            'is_protected',
  isRestricted:           'is_restricted',
  hashedPassword:         'hashed_password',
  resetPasswordToken:     'reset_password_token',
  resetPasswordSentAt:    'reset_password_sent_at',
  resetPasswordExpiresAt: 'reset_password_expires_at',
  frontendPreferences:    'frontend_preferences',
  preferences:            'preferences',
};

const USER_COLUMNS_MAPPING = {
  username:  (username) => {return username.toLowerCase()},
  createdAt: (timestamp) => {
    const d = new Date()
    d.setTime(timestamp)
    return d.toISOString()
  },
  updatedAt: (timestamp) => {
    const d = new Date()
    d.setTime(timestamp)
    return d.toISOString()
  },
  directsReadAt: (timestamp) => {
    const d = new Date()
    d.setTime(timestamp)
    return d.toISOString()
  },
  isPrivate:           (is_private) => {return is_private === '1'},
  isProtected:         (is_protected) => {return is_protected === '1'},
  isRestricted:        (is_restricted) => {return is_restricted === '1'},
  resetPasswordSentAt: (timestamp) => {
    const d = new Date()
    d.setTime(timestamp)
    return d.toISOString()
  }
};

const USER_FIELDS = {
  id:                        'intId',
  uid:                       'id',
  username:                  'username',
  screen_name:               'screenName',
  email:                     'email',
  description:               'description',
  type:                      'type',
  profile_picture_uuid:      'profilePictureUuid',
  created_at:                'createdAt',
  updated_at:                'updatedAt',
  directs_read_at:           'directsReadAt',
  notifications_read_at:     'notificationsReadAt',
  is_private:                'isPrivate',
  is_protected:              'isProtected',
  is_restricted:             'isRestricted',
  hashed_password:           'hashedPassword',
  reset_password_token:      'resetPasswordToken',
  reset_password_sent_at:    'resetPasswordSentAt',
  reset_password_expires_at: 'resetPasswordExpiresAt',
  frontend_preferences:      'frontendPreferences',
  subscribed_feed_ids:       'subscribedFeedIds',
  private_meta:              'privateMeta',
  preferences:               'preferences',
};

const USER_FIELDS_MAPPING = {
  created_at:                (time) => { return time.getTime().toString() },
  updated_at:                (time) => { return time.getTime().toString() },
  is_private:                (is_private) => {return is_private ? '1' : '0' },
  is_protected:              (is_protected) => {return is_protected ? '1' : '0' },
  is_restricted:             (is_restricted) => {return is_restricted ? '1' : '0' },
  reset_password_sent_at:    (time) => { return time && time.getTime() },
  reset_password_expires_at: (time) => { return time && time.getTime() },
  private_meta:              (data) => data || {}
};
<commit_msg>Wait for DB update before cache flush<commit_after>import _ from 'lodash';
import validator from 'validator'
import pgFormat from 'pg-format';

import { User, Group, Comment } from '../../models';
import { initObject, prepareModelPayload } from './utils';

const usersTrait = (superClass) => class extends superClass {
  async createUser(payload) {
    const preparedPayload = prepareModelPayload(payload, USER_COLUMNS, USER_COLUMNS_MAPPING)
    const [{ uid: uid, id: intId }] = await this.database('users').returning(['uid', 'id']).insert(preparedPayload);
    await this.createUserStats(uid)
    return [uid, intId];
  }

  async updateUser(userId, payload) {
    const tokenExpirationTime = new Date(Date.now())
    const expireAfter = 60 * 60 * 24 // 24 hours

    const preparedPayload = prepareModelPayload(payload, USER_COLUMNS, USER_COLUMNS_MAPPING)

    if (_.has(preparedPayload, 'reset_password_token')) {
      tokenExpirationTime.setHours(tokenExpirationTime.getHours() + expireAfter)
      preparedPayload['reset_password_expires_at'] = tokenExpirationTime.toISOString()
    }

    await this.database('users').where('uid', userId).update(preparedPayload)
    await this.cacheFlushUser(userId)
  }

  setUpdatedAtInGroupsByIds = async (groupIds, time) => {
    const updatedAt = new Date();
    updatedAt.setTime(time);

    const sql = pgFormat(`UPDATE "users" SET "updated_at" = ? WHERE "uid" IN (%L) AND "type"='group' RETURNING "uid"`, groupIds);
    const uids = await this.database.raw(sql, [updatedAt.toISOString()]);

    const flushPromises = uids.rows.map((row) => this.cacheFlushUser(row.uid));
    await Promise.all(flushPromises);
  };

  async existsUser(userId) {
    const res = await this.database('users').where('uid', userId).count()
    return parseInt(res[0].count)
  }

  async existsUsername(username) {
    const res = await this.database('users').where('username', username).count()
    return parseInt(res[0].count)
  }

  async existsUserEmail(email) {
    const res = await this.database('users').whereRaw('LOWER(email)=LOWER(?)', email).count()
    return parseInt(res[0].count)
  }

  async getUserById(id) {
    const user = await this.getFeedOwnerById(id)

    if (!user) {
      return null
    }

    if (!(user instanceof User)) {
      throw new Error(`Expected User, got ${user.constructor.name}`)
    }

    return user
  }

  async getUsersByIds(userIds) {
    const users = await this.getFeedOwnersByIds(userIds)

    _.each(users, (user) => {
      if (!(user instanceof User)) {
        throw new Error(`Expected User, got ${user.constructor.name}`)
      }
    })

    return users
  }

  async getUserByUsername(username) {
    const feed = await this.getFeedOwnerByUsername(username)

    if (null === feed) {
      return null
    }

    if (!(feed instanceof User)) {
      throw new Error(`Expected User, got ${feed.constructor.name}`)
    }

    return feed
  }

  async getUserByResetToken(token) {
    const attrs = await this.database('users').first().where('reset_password_token', token)

    if (!attrs) {
      return null
    }

    if (attrs.type !== 'user') {
      throw new Error(`Expected User, got ${attrs.type}`)
    }

    const now = new Date().getTime()
    if (attrs.reset_password_expires_at < now) {
      return null
    }

    return initUserObject(attrs);
  }

  async getUserByEmail(email) {
    const attrs = await this.database('users').first().whereRaw('LOWER(email)=LOWER(?)', email)

    if (!attrs) {
      return null
    }

    if (attrs.type !== 'user') {
      throw new Error(`Expected User, got ${attrs.type}`)
    }

    return initUserObject(attrs);
  }

  async _getUserIntIdByUUID(userUUID) {
    if (!validator.isUUID(userUUID, 4)) {
      return null;
    }

    const res = await this.database('users').returning('id').first().where('uid', userUUID);
    if (!res) {
      return null;
    }
    return res.id;
  }

  async getFeedOwnerById(id) {
    if (!validator.isUUID(id, 4)) {
      return null
    }
    return initUserObject(await this.fetchUser(id));
  }

  async getFeedOwnersByIds(ids) {
    return (await this.fetchUsers(ids)).map(initUserObject);
  }

  async getUsersByIdsAssoc(ids) {
    return _.mapValues(await this.fetchUsersAssoc(ids), initUserObject);
  }

  getUsersIdsByIntIds(intIds) {
    return this.database('users').select('id', 'uid').whereIn('id', intIds);
  }

  async getFeedOwnerByUsername(username) {
    const attrs = await this.database('users').first().where('username', username.toLowerCase())
    return initUserObject(attrs);
  }

  async getFeedOwnersByUsernames(usernames) {
    usernames = usernames.map((u) => u.toLowerCase());
    const users = await this.database('users').whereIn('username', usernames);
    return users.map(initUserObject);
  }

  async getGroupById(id) {
    const user = await this.getFeedOwnerById(id)

    if (!user) {
      return null
    }

    if (!(user instanceof Group)) {
      throw new Error(`Expected Group, got ${user.constructor.name}`)
    }

    return user
  }

  async getGroupByUsername(username) {
    const feed = await this.getFeedOwnerByUsername(username)

    if (null === feed) {
      return null
    }

    if (!(feed instanceof Group)) {
      throw new Error(`Expected Group, got ${feed.constructor.name}`)
    }

    return feed
  }

  async getUserSubscribersIds(userId) {
    return await this.database
      .pluck('s.user_id')
      .from('subscriptions as s')
      .innerJoin('feeds as f', 'f.uid', 's.feed_id')
      .where('f.name', 'Posts')
      .where('f.user_id', userId)
      .orderBy('s.created_at', 'desc');
  }

  // Insert record to 'archives' table for the test purposes.
  // 'params' should hold optional 'archives' fields.
  async setUserArchiveParams(userId, oldUsername, params = {}) {
    return await this.database('archives').insert({ ...params, user_id: userId, old_username: oldUsername });
  }

  // Return data from 'archives' table for the 'whoami' response
  async getUserArchiveParams(userId) {
    const params = await this.database('archives')
      .first('old_username', 'has_archive', 'via_sources', 'recovery_status', 'restore_comments_and_likes')
      .where({ user_id: userId });
    if (!params) {
      return null;
    }
    params.hidden_comments_count = 0;
    if (!params.restore_comments_and_likes) {
      const sql = `select count(*) from
        hidden_comments h
        join comments c on c.uid = h.comment_id
        where c.hide_type = :hideType and (h.user_id = :userId or h.old_username = :oldUsername)`;
      const res = await this.database.raw(sql, { hideType: Comment.HIDDEN_ARCHIVED, userId, oldUsername: params.old_username });
      params.hidden_comments_count = parseInt(res.rows[0].count);
    }
    return params;
  }

  async startArchiveRestoration(userId, params = {}) {
    params = {
      disable_comments: false,
      via_restore:      [],
      ...params,
      recovery_status:  1,
    };
    await this.database('archives').where('user_id', userId).update(params);
  }

  async enableArchivedActivitiesRestoration(userId) {
    await this.database('archives').where('user_id', userId).update({ restore_comments_and_likes: true });
  }

  async someUsersArePublic(userIds, anonymousFriendly) {
    if (userIds.length === 0) {
      return false;
    }
    const { rows } = await this.database.raw(
      `select 1 from users
      where
        not is_private
        and not (:anonymousFriendly and is_protected)
        and uid = any(:userIds)
      limit 1
      `,
      { userIds, anonymousFriendly }
    );
    return rows.length > 0;
  }

  /**
   * Returns UIDs of users who cah see any of
   * the given feeds. It is assumed that feeds
   * are private 'Posts' or 'Directs' feeds.
   *
   * @param {number[]} feedIntIds
   * @return {string[]}
   */
  async getUsersWhoCanSeePrivateFeeds(feedIntIds) {
    const { rows } = await this.database.raw(
      `
      -- Feed owners always can see these feeds
      select user_id from feeds where id = any(:feedIntIds)
      union
      -- Users who subscribed to feeds
      select s.user_id from
        subscriptions s
        join feeds f on f.uid = s.feed_id
      where f.id = any(:feedIntIds)
      `,
      { feedIntIds }
    );
    return _.uniq(_.map(rows, 'user_id'));
  }

  async getNotificationsDigestRecipients() {
    const users = await this.database('users')
      .where('type', 'user')
      .whereRaw(`preferences -> 'sendNotificationsDigest' = 'true'::jsonb`);
    return users.map(initUserObject);
  }
};

export default usersTrait;

///////////////////////////////////////////////////

export function initUserObject(attrs) {
  if (!attrs) {
    return null;
  }
  attrs = prepareModelPayload(attrs, USER_FIELDS, USER_FIELDS_MAPPING);
  return initObject(attrs.type === 'group' ? Group : User, attrs, attrs.id);
}

const USER_COLUMNS = {
  username:               'username',
  screenName:             'screen_name',
  email:                  'email',
  description:            'description',
  type:                   'type',
  profilePictureUuid:     'profile_picture_uuid',
  createdAt:              'created_at',
  updatedAt:              'updated_at',
  directsReadAt:          'directs_read_at',
  isPrivate:              'is_private',
  isProtected:            'is_protected',
  isRestricted:           'is_restricted',
  hashedPassword:         'hashed_password',
  resetPasswordToken:     'reset_password_token',
  resetPasswordSentAt:    'reset_password_sent_at',
  resetPasswordExpiresAt: 'reset_password_expires_at',
  frontendPreferences:    'frontend_preferences',
  preferences:            'preferences',
};

const USER_COLUMNS_MAPPING = {
  username:  (username) => {return username.toLowerCase()},
  createdAt: (timestamp) => {
    const d = new Date()
    d.setTime(timestamp)
    return d.toISOString()
  },
  updatedAt: (timestamp) => {
    const d = new Date()
    d.setTime(timestamp)
    return d.toISOString()
  },
  directsReadAt: (timestamp) => {
    const d = new Date()
    d.setTime(timestamp)
    return d.toISOString()
  },
  isPrivate:           (is_private) => {return is_private === '1'},
  isProtected:         (is_protected) => {return is_protected === '1'},
  isRestricted:        (is_restricted) => {return is_restricted === '1'},
  resetPasswordSentAt: (timestamp) => {
    const d = new Date()
    d.setTime(timestamp)
    return d.toISOString()
  }
};

const USER_FIELDS = {
  id:                        'intId',
  uid:                       'id',
  username:                  'username',
  screen_name:               'screenName',
  email:                     'email',
  description:               'description',
  type:                      'type',
  profile_picture_uuid:      'profilePictureUuid',
  created_at:                'createdAt',
  updated_at:                'updatedAt',
  directs_read_at:           'directsReadAt',
  notifications_read_at:     'notificationsReadAt',
  is_private:                'isPrivate',
  is_protected:              'isProtected',
  is_restricted:             'isRestricted',
  hashed_password:           'hashedPassword',
  reset_password_token:      'resetPasswordToken',
  reset_password_sent_at:    'resetPasswordSentAt',
  reset_password_expires_at: 'resetPasswordExpiresAt',
  frontend_preferences:      'frontendPreferences',
  subscribed_feed_ids:       'subscribedFeedIds',
  private_meta:              'privateMeta',
  preferences:               'preferences',
};

const USER_FIELDS_MAPPING = {
  created_at:                (time) => { return time.getTime().toString() },
  updated_at:                (time) => { return time.getTime().toString() },
  is_private:                (is_private) => {return is_private ? '1' : '0' },
  is_protected:              (is_protected) => {return is_protected ? '1' : '0' },
  is_restricted:             (is_restricted) => {return is_restricted ? '1' : '0' },
  reset_password_sent_at:    (time) => { return time && time.getTime() },
  reset_password_expires_at: (time) => { return time && time.getTime() },
  private_meta:              (data) => data || {}
};
<|endoftext|>


<commit_before>/*
* To use this with require.js AND the node.js module system (on server and client side).
* see https://github.com/jrburke/amdefine
*/
if (typeof define !== 'function') { var define = require('amdefine')(module); }

/**
 * This module contains all classes and utils that are useful
 * for working with multiple screens. Currently that's only the
 * {@link module:shared/screen.ScreenArranger ScreenArranger}.
 * @module shared/screen
 */
define(function(require, exports, module) {

	exports.Screen = function (top, left, player) {
		this.width = player.width;
		this.height = player.height;
		this.player = player;
		this.x = this.left = left;
		this.y = this.top = top;
	}

	exports.Screen.prototype.isHit = function (x, y) {

	};

	/**
	 * @classdesc This class can be used to arrange multiple clients
	 * into one big game screen. It does rearrange every time a player
	 * joins or leaves the given session.

	 * You can influence the behaviour by using this class as the base
	 * of your own Arranger class.
	 * @class
	 * @param {module:client/session~Session|module:server/session~Session}
	 *  Session that contains the players that should be arranged into
	 *  one big screen.
	 */
	exports.ScreenArranger = function (session) {
		/**
		 * Session that is getting arranged into one big game screen
		 * @type {module:client/session~Session|module:server/session~Session}
		 * @readonly
		 */
		this.session = session;
		/**
		 * Contains one screen object for every player id
		 * A screen object consists of player, x and y attributes.
		 * @type {Object.<string, object>}
		 * @private
		 */
		this.screens = {};
		/**
		 * total width of the big screen in pixel
		 * @type {integer}
		 * @readonly
		 */
		this.width = 0;
		/**
		 * total height of the big screen in pixel
		 * @type {integer}
		 * @readonly
		 */
		this.height = 0;

		this.arrange();
		session.on('playerJoined', this.onPlayerJoined.bind(this));
		session.on('playerLeft', this.onPlayerLeft.bind(this));
	};

	/**
	 * @param  {integer} x  current global x position in pixel
	 * @param  {integer} y  current global y position in pixel
	 * @param  {integer} step  number of pixels you want to move
	 * @return {object}  the new global position after moving step pixels
	 *  to the top. Values lower than zero will continue on the bottom.
	 */
	exports.ScreenArranger.prototype.getUp = function (x, y, step) {
		var newY = y - step;
		if (newY < 0) {
			newY = this.height + newY;
		}
		return { x: x, y: newY };
	};

	/**
	 * @param  {integer} x  current global x position in pixel
	 * @param  {integer} y  current global y position in pixel
	 * @param  {integer} step  number of pixels you want to move
	 * @return {object}  the new global position after moving step pixels
	 *  to the right. Values greater than the total width will continue
	 *  on the left side.
	 */
	exports.ScreenArranger.prototype.getRight = function (x, y, step) {
		var newX = x + step;
		if (newX >= this.width) {
			newX = this.width - newX;
		}
		return { x: newX, y: y };
	};

	/**
	 * @param  {integer} x  current global x position in pixel
	 * @param  {integer} y  current global y position in pixel
	 * @param  {integer} step  number of pixels you want to move
	 * @return {object}  the new global position after moving step pixels
	 *  to the bottom. Values lower than the total height will continue
	 *  on the top.
	 */
	exports.ScreenArranger.prototype.getDown = function (x, y, step) {
		var newY = y + step;
		if (newY >= this.height) {
			newY = newY - this.height;
		}
		return { x: x, y: newY };
	};

	/**
	 * @param  {integer} x  current global x position in pixel
	 * @param  {integer} y  current global y position in pixel
	 * @param  {integer} step  number of pixels you want to move
	 * @return {object}  the new global position after moving step pixels
	 *  to the left. Values lower than zero will continue on the right side.
	 */
	exports.ScreenArranger.prototype.getLeft = function (x, y, step) {
		var newX = x - step;
		if (newX < 0) {
			newX = this.width + newX;
		}
		return { x: newX, y: y };
	};

	/**
	 * Converts local pixel coordinates to global ones.
	 * @param  {module:server/player~Player|module:client/player~Player} player
	 * player instance the local coordinates refer to
	 * @param  {integer} x  local x position in pixel
	 * @param  {integer} y  local y position in pixel
	 * @return {object}  { x: globalX, y: globalY } or null if the given
	 *  player is no part of this arranger
	 */
	exports.ScreenArranger.prototype.localToGlobal = function (player, x, y) {
		var screen = this.screens[player.id];
		if (screen === null) {
			return null;
		}
		var globalX = screen.left + x;
		var globalY = screen.top + y;
		return {x: globalX, y: globalY };
	};

	exports.ScreenArranger.prototype.globalToLocals = function (x, y, witdh, height) {
		var players = [];
		players.push(this.getPlayerAtCoords(x, y));
		players.push(this.getPlayerAtCoords(x, y+height));
		players.push(this.getPlayerAtCoords(x+witdh, y));
		players.push(this.getPlayerAtCoords(x+witdh, y+height));

		var locals = {};
		var local, player;
		for (var i in players) {
			player = players[i];
			if (player !== null && !locals.hasOwnProperty(player.id)) {
				local = this.globalToLocal(x, y, player);
				if (local !== null) {
					locals[player.id] = { player: player, x: local.x, y: local.y };
				}
			}
		}

		return locals;
	};

	/**
	 * Converts global pixel coordinates into their corresponding
	 * local ones.
	 * @param  {integer} x  global x position in pixel
	 * @param  {integer} y  global y position in pixel
	 * @return {object}  { player: localPlayer, x: localX, y: localY }
	 *  or null if there is no player hitting the given coordinates
	 */
	exports.ScreenArranger.prototype.globalToLocal = function (x, y, player) {
		if (typeof player === 'undefined') {
			player = this.getPlayerAtCoords(x, y);
			if (player === null) {
				return null;
			}
		}
		var screen = this.screens[player.id];
		if (typeof screen !== 'undefined') {
			var localX = x - screen.left;
			var localY = y - screen.top;
			return {player: player, x: localX, y: localY };
		} else {
			return null;
		}
	};

	/**
	 * @param  {object}  screen  any screen object that should be tested
	 * @param  {integer}  x  global x position in pixel
	 * @param  {integer}  y  global y position in pixel
	 * @return {boolean}  true if the given global coordinates lie inside
	 * the given screen object, else false
	 * @private
	 */
	exports.ScreenArranger.prototype.isScreenHit = function (screen, x, y) {
		return x >= screen.left &&
			x < screen.left + screen.player.width &&
			y >= screen.top &&
			y < screen.top + screen.player.height;
	};

	/**
	 * @param  {module:server/player~Player|module:client/player~Player} player
	 * any player object connected to the arranged session
	 * @param  {integer}  x  global x position in pixel
	 * @param  {integer}  y  global y position in pixel
	 * @return {boolean}  true if the given coordinates lie within
	 * the screen of the given player
	 */
	exports.ScreenArranger.prototype.isPlayerHit = function (player, x, y) {
		return this.isScreenHit(this.screens[player.id], x, y);
	};

	/**
	 * @param  {integer}  x  global x position in pixel
	 * @param  {integer}  y  global y position in pixel
	 * @return {module:server/player~Player|module:client/player~Player}
	 * player object whose screen lies beneath the given coordinates
	 * or null when no player can be found at this position
	 */
	exports.ScreenArranger.prototype.getPlayerAtCoords = function (x, y) {
		for (var i in this.screens) {
			var screen = this.screens[i];
			if (this.isScreenHit(screen, x, y)) {
				return screen.player;
			}
		}
		return null;
	};

	/**
	 * Rearranges the screens of all players currently connected to
	 * the arranged session. Width, height and other attributes
	 * will be recalculated and may change.
	 * @private
	 */
	exports.ScreenArranger.prototype.arrange = function () {
		var screens = {};
		var height = 0;
		var xPos = 0;
		this.session.getPlayerArray().forEach(function (player) {
			screens[player.id] = new exports.Screen(0, xPos, player);
			height = Math.max(height, player.height);
			xPos += player.width;
		});

		this.screens = screens;
		this.width = xPos;
		this.height = height;
	};

	exports.ScreenArranger.prototype.onPlayerJoined = function (event) {
		this.arrange();
	};

	exports.ScreenArranger.prototype.onPlayerLeft = function (event) {
		this.arrange();
	};

	return exports;

});<commit_msg>moved "isHit" method to screen<commit_after>/*
* To use this with require.js AND the node.js module system (on server and client side).
* see https://github.com/jrburke/amdefine
*/
if (typeof define !== 'function') { var define = require('amdefine')(module); }

/**
 * This module contains all classes and utils that are useful
 * for working with multiple screens. Currently that's only the
 * {@link module:shared/screen.ScreenArranger ScreenArranger}.
 * @module shared/screen
 */
define(function(require, exports, module) {

	exports.Screen = function (top, left, player) {
		this.width = player.width;
		this.height = player.height;
		this.player = player;
		this.x = this.left = left;
		this.y = this.top = top;
	}

	/**
	 * @param  {integer}  x  global x position in pixel
	 * @param  {integer}  y  global y position in pixel
	 * @return {boolean}  true if the given global coordinates lie inside
	 * the this screen object, else false
	 * @private
	 */
	exports.Screen.prototype.isHit = function (x, y) {
		return x >= this.x &&
			x < this.x + this.width &&
			y >= this.y &&
			y < this.y + this.height;
	};

	/**
	 * @classdesc This class can be used to arrange multiple clients
	 * into one big game screen. It does rearrange every time a player
	 * joins or leaves the given session.

	 * You can influence the behaviour by using this class as the base
	 * of your own Arranger class.
	 * @class
	 * @param {module:client/session~Session|module:server/session~Session}
	 *  Session that contains the players that should be arranged into
	 *  one big screen.
	 */
	exports.ScreenArranger = function (session) {
		/**
		 * Session that is getting arranged into one big game screen
		 * @type {module:client/session~Session|module:server/session~Session}
		 * @readonly
		 */
		this.session = session;
		/**
		 * Contains one screen object for every player id
		 * A screen object consists of player, x and y attributes.
		 * @type {Object.<string, object>}
		 * @private
		 */
		this.screens = {};
		/**
		 * total width of the big screen in pixel
		 * @type {integer}
		 * @readonly
		 */
		this.width = 0;
		/**
		 * total height of the big screen in pixel
		 * @type {integer}
		 * @readonly
		 */
		this.height = 0;

		this.arrange();
		session.on('playerJoined', this.onPlayerJoined.bind(this));
		session.on('playerLeft', this.onPlayerLeft.bind(this));
	};

	/**
	 * @param  {integer} x  current global x position in pixel
	 * @param  {integer} y  current global y position in pixel
	 * @param  {integer} step  number of pixels you want to move
	 * @return {object}  the new global position after moving step pixels
	 *  to the top. Values lower than zero will continue on the bottom.
	 */
	exports.ScreenArranger.prototype.getUp = function (x, y, step) {
		var newY = y - step;
		if (newY < 0) {
			newY = this.height + newY;
		}
		return { x: x, y: newY };
	};

	/**
	 * @param  {integer} x  current global x position in pixel
	 * @param  {integer} y  current global y position in pixel
	 * @param  {integer} step  number of pixels you want to move
	 * @return {object}  the new global position after moving step pixels
	 *  to the right. Values greater than the total width will continue
	 *  on the left side.
	 */
	exports.ScreenArranger.prototype.getRight = function (x, y, step) {
		var newX = x + step;
		if (newX >= this.width) {
			newX = this.width - newX;
		}
		return { x: newX, y: y };
	};

	/**
	 * @param  {integer} x  current global x position in pixel
	 * @param  {integer} y  current global y position in pixel
	 * @param  {integer} step  number of pixels you want to move
	 * @return {object}  the new global position after moving step pixels
	 *  to the bottom. Values lower than the total height will continue
	 *  on the top.
	 */
	exports.ScreenArranger.prototype.getDown = function (x, y, step) {
		var newY = y + step;
		if (newY >= this.height) {
			newY = newY - this.height;
		}
		return { x: x, y: newY };
	};

	/**
	 * @param  {integer} x  current global x position in pixel
	 * @param  {integer} y  current global y position in pixel
	 * @param  {integer} step  number of pixels you want to move
	 * @return {object}  the new global position after moving step pixels
	 *  to the left. Values lower than zero will continue on the right side.
	 */
	exports.ScreenArranger.prototype.getLeft = function (x, y, step) {
		var newX = x - step;
		if (newX < 0) {
			newX = this.width + newX;
		}
		return { x: newX, y: y };
	};

	/**
	 * Converts local pixel coordinates to global ones.
	 * @param  {module:server/player~Player|module:client/player~Player} player
	 * player instance the local coordinates refer to
	 * @param  {integer} x  local x position in pixel
	 * @param  {integer} y  local y position in pixel
	 * @return {object}  { x: globalX, y: globalY } or null if the given
	 *  player is no part of this arranger
	 */
	exports.ScreenArranger.prototype.localToGlobal = function (player, x, y) {
		var screen = this.screens[player.id];
		if (screen === null) {
			return null;
		}
		var globalX = screen.left + x;
		var globalY = screen.top + y;
		return {x: globalX, y: globalY };
	};

	exports.ScreenArranger.prototype.globalToLocals = function (x, y, witdh, height) {
		var players = [];
		players.push(this.getPlayerAtCoords(x, y));
		players.push(this.getPlayerAtCoords(x, y+height));
		players.push(this.getPlayerAtCoords(x+witdh, y));
		players.push(this.getPlayerAtCoords(x+witdh, y+height));

		var locals = {};
		var local, player;
		for (var i in players) {
			player = players[i];
			if (player !== null && !locals.hasOwnProperty(player.id)) {
				local = this.globalToLocal(x, y, player);
				if (local !== null) {
					locals[player.id] = { player: player, x: local.x, y: local.y };
				}
			}
		}

		return locals;
	};

	/**
	 * Converts global pixel coordinates into their corresponding
	 * local ones.
	 * @param  {integer} x  global x position in pixel
	 * @param  {integer} y  global y position in pixel
	 * @return {object}  { player: localPlayer, x: localX, y: localY }
	 *  or null if there is no player hitting the given coordinates
	 */
	exports.ScreenArranger.prototype.globalToLocal = function (x, y, player) {
		if (typeof player === 'undefined') {
			player = this.getPlayerAtCoords(x, y);
			if (player === null) {
				return null;
			}
		}
		var screen = this.screens[player.id];
		if (typeof screen !== 'undefined') {
			var localX = x - screen.left;
			var localY = y - screen.top;
			return {player: player, x: localX, y: localY };
		} else {
			return null;
		}
	};

	/**
	 * @param  {module:server/player~Player|module:client/player~Player} player
	 * any player object connected to the arranged session
	 * @param  {integer}  x  global x position in pixel
	 * @param  {integer}  y  global y position in pixel
	 * @return {boolean}  true if the given coordinates lie within
	 * the screen of the given player
	 */
	exports.ScreenArranger.prototype.isPlayerHit = function (player, x, y) {
		return this.screens[player.id].isHit(x, y);
	};

	/**
	 * @param  {integer}  x  global x position in pixel
	 * @param  {integer}  y  global y position in pixel
	 * @return {module:server/player~Player|module:client/player~Player}
	 * player object whose screen lies beneath the given coordinates
	 * or null when no player can be found at this position
	 */
	exports.ScreenArranger.prototype.getPlayerAtCoords = function (x, y) {
		for (var i in this.screens) {
			var screen = this.screens[i];
			if (screen.isHit(x, y)) {
				return screen.player;
			}
		}
		return null;
	};

	/**
	 * Rearranges the screens of all players currently connected to
	 * the arranged session. Width, height and other attributes
	 * will be recalculated and may change.
	 * @private
	 */
	exports.ScreenArranger.prototype.arrange = function () {
		var screens = {};
		var height = 0;
		var xPos = 0;
		this.session.getPlayerArray().forEach(function (player) {
			screens[player.id] = new exports.Screen(0, xPos, player);
			height = Math.max(height, player.height);
			xPos += player.width;
		});

		this.screens = screens;
		this.width = xPos;
		this.height = height;
	};

	exports.ScreenArranger.prototype.onPlayerJoined = function (event) {
		this.arrange();
	};

	exports.ScreenArranger.prototype.onPlayerLeft = function (event) {
		this.arrange();
	};

	return exports;

});<|endoftext|>


<commit_before>import Validator from 'furnace-validation/validators/property';
import Ember from 'ember';
export default Validator.extend({

	messages : {	isNaN : 'isNaN' ,
					notReal : 'notReal',
					isReal : 'isReal',
					tooSmall : 'tooSmall',
					tooLarge : 'tooLarge',
					outOfRange : 'outOfRange'
				},

	real: null,
	min : null,
	max : null,
	range : null,

	call : function(context,value,result) {
		if(!Ember.isBlank(value) && !value===false) {
			if(value==='-') {
				result.addError(context,this.messages.isNaN,null,'delayed');
			} else if(isNaN(value)) {
				result.addError(context,this.messages.isNaN,null,'immediate');
			} else if(this.real===false && value>0) {
				result.addError(context,this.messages.isReal,null,'immediate');
			} else if(this.real===true && value<0) {
				result.addError(context,this.messages.notReal,null,'immediate');
			} else if(this.range!==null) {
				if(value<this.range[0] || value>this.range[1]) {
					result.addError(context,this.messages.outOfRange,[this.range[0],this.range[1]],'immediate');
				}
			} else if(this.min!==null && value<this.min) {
				result.addError(context,this.messages.tooSmall,[this.min],'immediate');
			} else if(this.max!==null && value>this.max) {
				result.addError(context,this.messages.tooLarge,[this.max],'immediate');
			}
		}
	}
});
<commit_msg>Fixed bug in number validator "false" check<commit_after>import Validator from 'furnace-validation/validators/property';
import Ember from 'ember';
export default Validator.extend({

	messages : {	isNaN : 'isNaN' ,
					notReal : 'notReal',
					isReal : 'isReal',
					tooSmall : 'tooSmall',
					tooLarge : 'tooLarge',
					outOfRange : 'outOfRange'
				},

	real: null,
	min : null,
	max : null,
	range : null,

	call : function(context,value,result) {
		if(!Ember.isBlank(value) && value!==false) {
			if(value==='-') {
				result.addError(context,this.messages.isNaN,null,'delayed');
			} else if(isNaN(value)) {
				result.addError(context,this.messages.isNaN,null,'immediate');
			} else if(this.real===false && value>0) {
				result.addError(context,this.messages.isReal,null,'immediate');
			} else if(this.real===true && value<0) {
				result.addError(context,this.messages.notReal,null,'immediate');
			} else if(this.range!==null) {
				if(value<this.range[0] || value>this.range[1]) {
					result.addError(context,this.messages.outOfRange,[this.range[0],this.range[1]],'immediate');
				}
			} else if(this.min!==null && value<this.min) {
				result.addError(context,this.messages.tooSmall,[this.min],'immediate');
			} else if(this.max!==null && value>this.max) {
				result.addError(context,this.messages.tooLarge,[this.max],'immediate');
			}
		}
	}
});
<|endoftext|>


<commit_before>const gulpConnect = require('gulp-connect');
const gulpLess = require('gulp-less');
const gulpRename = require('gulp-rename');
const gulpSourcemaps = require('gulp-sourcemaps');
const mergeStream = require('merge-stream');
const path = require('path');
const {getDestDir} = require('./paths');
const {logError} = require('./utils');

module.exports = function createCSSBundleTasks(gulp) {
    gulp.task('css-release', () => bundleCSS({production: true}));
    gulp.task('css-debug', () => bundleCSS({production: false}));

    function bundleCSS({production}) {
        const dir = getDestDir({production});
        const files = {
            'src/ui/devtools/style.less': `${dir}/ui/devtools/style.css`,
            'src/ui/popup/style.less': `${dir}/ui/popup/style.css`,
            'src/ui/stylesheet-editor/style.less': `${dir}/ui/stylesheet-editor/style.css`,
        };
        const bundles = Object.entries(files).map(([src, dest]) => bundleCSSEntry({src, dest, production}));
        return mergeStream(...bundles)
            .pipe(gulpConnect.reload());
    }

    function bundleCSSEntry({src, dest, production}) {
        return gulp.src(src)
            .pipe(gulpSourcemaps.init())
            .pipe(gulpLess())
            .on('error', function (err) {
                logError(err);
                this.emit('end');
            })
            .pipe(gulpSourcemaps.write())
            .pipe(gulpRename(path.basename(dest)))
            .pipe(gulp.dest(path.dirname(dest)));
    }
};
<commit_msg>Fixed including CSS sourcemaps into build<commit_after>const gulpConnect = require('gulp-connect');
const gulpLess = require('gulp-less');
const gulpRename = require('gulp-rename');
const gulpSourcemaps = require('gulp-sourcemaps');
const mergeStream = require('merge-stream');
const path = require('path');
const {getDestDir} = require('./paths');
const {logError} = require('./utils');

module.exports = function createCSSBundleTasks(gulp) {
    gulp.task('css-release', () => bundleCSS({production: true}));
    gulp.task('css-debug', () => bundleCSS({production: false}));

    function bundleCSS({production}) {
        const dir = getDestDir({production});
        const files = {
            'src/ui/devtools/style.less': `${dir}/ui/devtools/style.css`,
            'src/ui/popup/style.less': `${dir}/ui/popup/style.css`,
            'src/ui/stylesheet-editor/style.less': `${dir}/ui/stylesheet-editor/style.css`,
        };
        const bundles = Object.entries(files).map(([src, dest]) => bundleCSSEntry({src, dest, production}));
        return mergeStream(...bundles)
            .pipe(gulpConnect.reload());
    }

    function bundleCSSEntry({src, dest, production}) {
        let stream = gulp.src(src)
            .pipe(gulpSourcemaps.init())
            .pipe(gulpLess())
            .on('error', function (err) {
                logError(err);
                this.emit('end');
            });
        if (!production) {
            stream = stream.pipe(gulpSourcemaps.write());
        }
        return stream
            .pipe(gulpRename(path.basename(dest)))
            .pipe(gulp.dest(path.dirname(dest)));
    }
};
<|endoftext|>