<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

'use strict';

const async = require('async');
const debug = require('debug')('engine_util');
const traverse = require('traverse');
const esprima = require('esprima');
const L = require('lodash');
const vm = require('vm');
const A = require('async');
const jsonpath = require('JSONPath');
const cheerio = require('cheerio');
const jitter = require('./jitter').jitter;

let xmlCapture;
try {
  xmlCapture = require('artillery-xml-capture');
} catch (e) {
  xmlCapture = null;
}

module.exports = {
  createThink: createThink,
  createLoopWithCount: createLoopWithCount,
  createParallel: createParallel,
  isProbableEnough: isProbableEnough,
  template: template,
  captureOrMatch,
  evil: evil,
  _renderVariables: renderVariables
};

function createThink(requestSpec, opts) {
  opts = opts || {};

  let thinkspec = requestSpec.think;

  let f = function think(context, callback) {
    let thinktime = parseFloat(template(thinkspec, context)) * 1000;
    if (requestSpec.jitter || opts.jitter) {
      thinktime = jitter(`${thinktime}:${requestSpec.jitter || opts.jitter}`);
    }
    debug('think %s, %s, %s -> %s', requestSpec.think, requestSpec.jitter, opts.jitter, thinktime);
    setTimeout(function() {
      callback(null, context);
    }, thinktime);
  };

  return f;
}

// "count" can be an integer (negative or positive) or a string defining a range
// like "1-15"
function createLoopWithCount(count, steps, opts) {
  let from = parseLoopCount(count).from;
  let to = parseLoopCount(count).to;

  return function aLoop(context, callback) {
    let i = from;
    let newContext = context;
    let loopIndexVar = (opts && opts.loopValue) || '$loopCount';
    let loopElementVar = (opts && opts.loopElement) || '$loopElement';
    // Should we stop early because the value of "over" is not an array
    let abortEarly = false;

    let overValues = null;
    let loopValue = null;
    if (typeof opts.overValues !== 'undefined') {
      if (opts.overValues && typeof opts.overValues === 'object') {
        overValues = opts.overValues;
        loopValue = overValues[i];
      } else if (opts.overValues && typeof opts.overValues === 'string') {
        overValues = context.vars[opts.overValues];
        if (L.isArray(overValues)) {
          loopValue = overValues[i];
        } else {
          abortEarly = true;
        }
      }
    }

    newContext.vars[loopElementVar] = loopValue;
    newContext.vars[loopIndexVar] = i;

    let shouldContinue = true;

    A.whilst(
      function test() {
        if (abortEarly) {
          return false;
        }
        if (opts.whileTrue) {
          return shouldContinue;
        }
        if (overValues !== null) {
          return i !== overValues.length;
        } else {
          return i < to || to === -1;
        }
      },
      function repeated(cb) {
        let zero = function(cb2) {
          return cb2(null, newContext);
        };
        let steps2 = L.flatten([zero, steps]);

        A.waterfall(steps2, function(err, context2) {
          if (err) {
            return cb(err, context2);
          }
          i++;
          newContext = context2;
          newContext.vars[loopIndexVar]++;
          if (overValues !== null) {
            newContext.vars[loopElementVar] = overValues[i];
          }

          if (opts.whileTrue) {
            opts.whileTrue(context2, function done(b) {
              shouldContinue = b;
              return cb(err, context2);
            });
          } else {
            return cb(err, context2);
          }
        });
      },
      function(err, finalContext) {
        return callback(err, finalContext);
      });
  };
}

function createParallel(steps, opts) {

  let limit = (opts && opts.limitValue) || 100;

  return function aParallel(context, callback) {
    let newContext = context;
    let newCallback = callback;

    // Remap the steps array to pass the context into each step.
    let newSteps = L.map(steps, function(step) {
      return function(callback){
        step(newContext, callback);
      };
    });

    // Run each of the steps in parallel.
    A.parallelLimit(
      newSteps,
      limit,
      function(err, finalContext) {
        // We don't need to do anything with the array of contexts returned from each step at the moment.
        return newCallback(err, newContext);
      });
  };
}

function isProbableEnough(obj) {
  if (typeof obj.probability === 'undefined') {
    return true;
  }

  let probability = Number(obj.probability) || 0;
  if (probability > 100) {
    probability = 100;
  }

  let r = L.random(100);
  return r < probability;
}

function template(o, context) {
  let result;
  if (typeof o === 'object') {
    result = traverse(o).map(function(x) {

      if (typeof x === 'string') {
        this.update(template(x, context));
      } else {
        return x;
      }
    });
  } else {
    if (!/{{/.test(o)) {
      return o;
    }
    const funcCallRegex = /{{\s*(\$[A-Za-z0-9_]+\s*\(\s*.*\s*\))\s*}}/;
    let match = o.match(funcCallRegex);
    if (match) {
      // This looks like it could be a function call:
      const syntax = esprima.parse(match[1]);
      // TODO: Use a proper schema for what we expect here
      if (syntax.body && syntax.body.length === 1 &&
          syntax.body[0].type === 'ExpressionStatement') {
        let funcName = syntax.body[0].expression.callee.name;
        let args = L.map(syntax.body[0].expression.arguments, function(arg) {
          return arg.value;
        });
        if (funcName in context.funcs) {
          return template(o.replace(funcCallRegex, context.funcs[funcName].apply(null, args)), context);
        }
      }
    } else {
      if (!o.match(/{{/)) {
        return o;
      }

      result = renderVariables(o, context.vars);
    }
  }
  return result;
}

function renderVariables (str, vars) {
  const RX = /{{{?[\s$\w]+}}}?/g;
  let rxmatch;
  let result = str.substring(0, str.length);

  // Special case for handling integer/boolean/object substitution:
  //
  // Does the template string contain one variable and nothing else?
  // e.g.: "{{ myvar }" or "{{    myvar }", but NOT " {{ myvar }"
  // If so, we treat it as a special case.
  const matches = str.match(RX);
  if (matches && matches.length === 1) {
    if (matches[0] === str) {
      // there's nothing else in the template but the variable
      const varName = str.replace(/{/g, '').replace(/}/g, '').trim();
      return vars[varName] || '';
    }
  }

  while (result.search(RX) > -1) {
    let templateStr = result.match(RX)[0];
    const varName = templateStr.replace(/{/g, '').replace(/}/g, '').trim();

    let varValue = vars[varName] || '';
    if (typeof varValue === 'object') {
      varValue = JSON.stringify(varValue);
    }
    result = result.replace(templateStr, varValue);
  }

  return result;
}

// Presume code is valid JS code (i.e. that it has been checked elsewhere)
function evil(sandbox, code) {
  let context = vm.createContext(sandbox);
  let script = new vm.Script(code);
  try {
    return script.runInContext(context);
  }
  catch(e) {
    return null;
  }
}

function parseLoopCount(countSpec) {
  let from = 0;
  let to = 0;

  if (typeof countSpec === 'number') {
    from = 0;
    to = countSpec;
  } else if (typeof countSpec === 'string') {
    if (isNaN(Number(countSpec))) {
      if (/\d\-\d/.test(countSpec)) {
        from = Number(countSpec.split('-')[0]);
        to = Number(countSpec.split('-')[1]);
      } else {
        to = 0;
      }
    } else {
      to = Number(countSpec);
    }
  } else {
    to = 0;
  }

  return { from: from, to: to };
}

//
// Calls done() with:
// {captures: { var: value }, matches: { var: {expected: '', got: ''} }}
//
function captureOrMatch(params, response, context, done) {
  let result = {
    captures: {},
    matches: {}
  };

  if (!params.capture && !params.match) {
    return done(null, result);
  }

  let specs = L.concat(
    L.get(params, 'capture', []),
    L.get(params, 'match', []));

  async.eachSeries(
    specs,
    function(spec, next) {
      let parsedSpec = parseSpec(spec, response);
      let parser = parsedSpec.parser;
      let extractor = parsedSpec.extractor;
      let expr = parsedSpec.expr;

      // are we looking at body or headers:
      var content = response.body;
      if (spec.header) {
        content = response.headers;
      }

      parser(content, function(err, doc) {
        if (err) {
          // TODO: The message could be improved still
          if (spec.strict) {
            return next(new Error(`Couldn't parse response for '${expr}'`), null);
          }
        }

        let extractedValue = extractor(doc, template(expr, context), spec);

        if (spec.value) {
          // this is a match spec
          let expected = template(spec.value, context);
          debug('match: %s, expected: %s, got: %s', expr, expected, extractedValue);
          if (extractedValue !== expected) {
            result.matches[expr] = {
              success: false,
              expected: expected,
              got: extractedValue,
              expression: expr,
              strict: spec.strict
            };
          } else {
            result.matches.expr = {
              success: true,
              expected: expected,
              expression: expr
            };
          }
          return next(null);
        }

        if (spec.as) {
          // this is a capture
          debug('capture: %s = %s', spec.as, extractedValue);
          result.captures[spec.as] = extractedValue;
          if (spec.transform) {
            let transformedValue = evil(
              result.captures,
              spec.transform);

            debug('transform: %s = %s', spec.as, result.captures[spec.as]);
            result.captures[spec.as] = transformedValue !== null ? transformedValue : extractedValue;
          }
        }

        return next(null);
      });
    },
    function(err) {
      if (err) {
        return done(err, null);
      } else {
        return done(null, result);
      }
    });
}

function parseSpec(spec, response) {
  let parser;
  let extractor;
  let expr;

  if (spec.json) {
    parser = parseJSON;
    extractor = extractJSONPath;
    expr = spec.json;
  } else if (xmlCapture && spec.xpath) {
    parser = xmlCapture.parseXML;
    extractor = xmlCapture.extractXPath;
    expr = spec.xpath;
  } else if (spec.regexp) {
    parser = dummyParser;
    extractor = extractRegExp;
    expr = spec.regexp;
  } else if (spec.header) {
    parser = dummyParser;
    extractor = extractHeader;
    expr = spec.header;
  } else if (spec.selector) {
    parser = dummyParser;
    extractor = extractCheerio;
    expr = spec.selector;
  } else {
    if (isJSON(response)) {
      parser = parseJSON;
      extractor = extractJSONPath;
      expr = spec.json;
    } else if (xmlCapture && isXML(response)) {
      parser = xmlCapture.parseXML;
      extractor = xmlCapture.extractXPath;
      expr = spec.xpath;
    } else {
      // We really don't know what to do here.
      parser = dummyParser;
      extractor = dummyExtractor;
      expr = '';
    }
  }

  return { parser: parser, extractor: extractor, expr: expr };
}

/*
 * Wrap JSON.parse in a callback
 */
function parseJSON(body, callback) {
  let r = null;
  let err = null;

  try {
    if (typeof body === 'string') {
      r = JSON.parse(body);
    } else {
      r = body;
    }
  } catch(e) {
    err = e;
  }

  return callback(err, r);
}

function dummyParser(body, callback) {
  return callback(null, body);
}

// doc is a JSON object
function extractJSONPath(doc, expr) {
  let results = jsonpath.eval(doc, expr);

  if (!results) {
    return '';
  }

  if (results.length > 1) {
    return results[randomInt(0, results.length - 1)];
  } else {
    return results[0];
  }
}

// doc is a string or an object (body parsed by Request when headers indicate JSON)
function extractRegExp(doc, expr, opts) {
  let group = opts.group;
  let flags = opts.flags;
  let str;
  if (typeof doc === 'string') {
    str = doc;
  } else {
    str = JSON.stringify(doc); // FIXME: not the same string as the one we got from the server
  }
  let rx;
  if (flags) {
      rx = new RegExp(expr, flags);
  } else {
      rx = new RegExp(expr);
  }
  let match = rx.exec(str);
  if (!match) {
    return '';
  }
  if(group && match[group]) {
    return match[group];
  } else if (match[0]) {
    return match[0];
  } else {
    return '';
  }
}

function extractHeader(headers, headerName) {
  return headers[headerName] || '';
}

function extractCheerio(doc, expr, opts) {
  let $ = cheerio.load(doc);
  let els = $(expr);
  let i = 0;
  if (typeof opts.index !== 'undefined') {
    if (opts.index === 'random') {
      i = Math.ceil(Math.random() * els.get().length - 1);
    } else if (opts.index === 'last') {
      i = els.get().length() - 1;
    } else if (typeof Number(opts.index) === 'number') {
      i = Number(opts.index);
    }
  }
  return els.slice(i, i + 1).attr(opts.attr);
}

function dummyExtractor() {
  return '';
}

/*
 * Given a response object determine if it's JSON
 */
function isJSON(res) {
  debug('isJSON: content-type = %s', res.headers['content-type']);
  return (res.headers['content-type'] &&
          /^application\/json/.test(res.headers['content-type']));
}

/*
 * Given a response object determine if it's some kind of XML
 */
function isXML(res) {
  return (res.headers['content-type'] &&
          (/^[a-zA-Z]+\/xml/.test(res.headers['content-type']) ||
           /^[a-zA-Z]+\/[a-zA-Z]+\+xml/.test(res.headers['content-type'])));

}

function randomInt (low, high) {
  return Math.floor(Math.random() * (high - low + 1) + low);
}
<commit_msg>fix: Handle nested loops where one of them does not execute<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

'use strict';

const async = require('async');
const debug = require('debug')('engine_util');
const traverse = require('traverse');
const esprima = require('esprima');
const L = require('lodash');
const vm = require('vm');
const A = require('async');
const jsonpath = require('JSONPath');
const cheerio = require('cheerio');
const jitter = require('./jitter').jitter;

let xmlCapture;
try {
  xmlCapture = require('artillery-xml-capture');
} catch (e) {
  xmlCapture = null;
}

module.exports = {
  createThink: createThink,
  createLoopWithCount: createLoopWithCount,
  createParallel: createParallel,
  isProbableEnough: isProbableEnough,
  template: template,
  captureOrMatch,
  evil: evil,
  _renderVariables: renderVariables
};

function createThink(requestSpec, opts) {
  opts = opts || {};

  let thinkspec = requestSpec.think;

  let f = function think(context, callback) {
    let thinktime = parseFloat(template(thinkspec, context)) * 1000;
    if (requestSpec.jitter || opts.jitter) {
      thinktime = jitter(`${thinktime}:${requestSpec.jitter || opts.jitter}`);
    }
    debug('think %s, %s, %s -> %s', requestSpec.think, requestSpec.jitter, opts.jitter, thinktime);
    setTimeout(function() {
      callback(null, context);
    }, thinktime);
  };

  return f;
}

// "count" can be an integer (negative or positive) or a string defining a range
// like "1-15"
function createLoopWithCount(count, steps, opts) {
  let from = parseLoopCount(count).from;
  let to = parseLoopCount(count).to;

  return function aLoop(context, callback) {
    let i = from;
    let newContext = context;
    let loopIndexVar = (opts && opts.loopValue) || '$loopCount';
    let loopElementVar = (opts && opts.loopElement) || '$loopElement';
    // Should we stop early because the value of "over" is not an array
    let abortEarly = false;

    let overValues = null;
    let loopValue = null;
    if (typeof opts.overValues !== 'undefined') {
      if (opts.overValues && typeof opts.overValues === 'object') {
        overValues = opts.overValues;
        loopValue = overValues[i];
      } else if (opts.overValues && typeof opts.overValues === 'string') {
        overValues = context.vars[opts.overValues];
        if (L.isArray(overValues)) {
          loopValue = overValues[i];
        } else {
          abortEarly = true;
        }
      }
    }

    newContext.vars[loopElementVar] = loopValue;
    newContext.vars[loopIndexVar] = i;

    let shouldContinue = true;

    A.whilst(
      function test() {
        if (abortEarly) {
          return false;
        }
        if (opts.whileTrue) {
          return shouldContinue;
        }
        if (overValues !== null) {
          return i !== overValues.length;
        } else {
          return i < to || to === -1;
        }
      },
      function repeated(cb) {
        let zero = function(cb2) {
          return cb2(null, newContext);
        };
        let steps2 = L.flatten([zero, steps]);

        A.waterfall(steps2, function(err, context2) {
          if (err) {
            return cb(err, context2);
          }
          i++;
          newContext = context2;

          newContext.vars[loopIndexVar]++;
          if (overValues !== null) {
            newContext.vars[loopElementVar] = overValues[i];
          }

          if (opts.whileTrue) {
            opts.whileTrue(context2, function done(b) {
              shouldContinue = b;
              return cb(err, context2);
            });
          } else {
            return cb(err, context2);
          }
        });
      },
      function(err, finalContext) {
        if (typeof finalContext === 'undefined') {
          // this happens if test() returns false immediately, e.g. with
          // nested loops where one of the inner loops goes over an
          // empty array
          return callback(err, newContext);
        }
        return callback(err, finalContext);
      });
  };
}

function createParallel(steps, opts) {

  let limit = (opts && opts.limitValue) || 100;

  return function aParallel(context, callback) {
    let newContext = context;
    let newCallback = callback;

    // Remap the steps array to pass the context into each step.
    let newSteps = L.map(steps, function(step) {
      return function(callback){
        step(newContext, callback);
      };
    });

    // Run each of the steps in parallel.
    A.parallelLimit(
      newSteps,
      limit,
      function(err, finalContext) {
        // We don't need to do anything with the array of contexts returned from each step at the moment.
        return newCallback(err, newContext);
      });
  };
}

function isProbableEnough(obj) {
  if (typeof obj.probability === 'undefined') {
    return true;
  }

  let probability = Number(obj.probability) || 0;
  if (probability > 100) {
    probability = 100;
  }

  let r = L.random(100);
  return r < probability;
}

function template(o, context) {
  let result;
  if (typeof o === 'object') {
    result = traverse(o).map(function(x) {

      if (typeof x === 'string') {
        this.update(template(x, context));
      } else {
        return x;
      }
    });
  } else {
    if (!/{{/.test(o)) {
      return o;
    }
    const funcCallRegex = /{{\s*(\$[A-Za-z0-9_]+\s*\(\s*.*\s*\))\s*}}/;
    let match = o.match(funcCallRegex);
    if (match) {
      // This looks like it could be a function call:
      const syntax = esprima.parse(match[1]);
      // TODO: Use a proper schema for what we expect here
      if (syntax.body && syntax.body.length === 1 &&
          syntax.body[0].type === 'ExpressionStatement') {
        let funcName = syntax.body[0].expression.callee.name;
        let args = L.map(syntax.body[0].expression.arguments, function(arg) {
          return arg.value;
        });
        if (funcName in context.funcs) {
          return template(o.replace(funcCallRegex, context.funcs[funcName].apply(null, args)), context);
        }
      }
    } else {
      if (!o.match(/{{/)) {
        return o;
      }

      result = renderVariables(o, context.vars);
    }
  }
  return result;
}

function renderVariables (str, vars) {
  const RX = /{{{?[\s$\w]+}}}?/g;
  let rxmatch;
  let result = str.substring(0, str.length);

  // Special case for handling integer/boolean/object substitution:
  //
  // Does the template string contain one variable and nothing else?
  // e.g.: "{{ myvar }" or "{{    myvar }", but NOT " {{ myvar }"
  // If so, we treat it as a special case.
  const matches = str.match(RX);
  if (matches && matches.length === 1) {
    if (matches[0] === str) {
      // there's nothing else in the template but the variable
      const varName = str.replace(/{/g, '').replace(/}/g, '').trim();
      return vars[varName] || '';
    }
  }

  while (result.search(RX) > -1) {
    let templateStr = result.match(RX)[0];
    const varName = templateStr.replace(/{/g, '').replace(/}/g, '').trim();

    let varValue = vars[varName] || '';
    if (typeof varValue === 'object') {
      varValue = JSON.stringify(varValue);
    }
    result = result.replace(templateStr, varValue);
  }

  return result;
}

// Presume code is valid JS code (i.e. that it has been checked elsewhere)
function evil(sandbox, code) {
  let context = vm.createContext(sandbox);
  let script = new vm.Script(code);
  try {
    return script.runInContext(context);
  }
  catch(e) {
    return null;
  }
}

function parseLoopCount(countSpec) {
  let from = 0;
  let to = 0;

  if (typeof countSpec === 'number') {
    from = 0;
    to = countSpec;
  } else if (typeof countSpec === 'string') {
    if (isNaN(Number(countSpec))) {
      if (/\d\-\d/.test(countSpec)) {
        from = Number(countSpec.split('-')[0]);
        to = Number(countSpec.split('-')[1]);
      } else {
        to = 0;
      }
    } else {
      to = Number(countSpec);
    }
  } else {
    to = 0;
  }

  return { from: from, to: to };
}

//
// Calls done() with:
// {captures: { var: value }, matches: { var: {expected: '', got: ''} }}
//
function captureOrMatch(params, response, context, done) {
  let result = {
    captures: {},
    matches: {}
  };

  if (!params.capture && !params.match) {
    return done(null, result);
  }

  let specs = L.concat(
    L.get(params, 'capture', []),
    L.get(params, 'match', []));

  async.eachSeries(
    specs,
    function(spec, next) {
      let parsedSpec = parseSpec(spec, response);
      let parser = parsedSpec.parser;
      let extractor = parsedSpec.extractor;
      let expr = parsedSpec.expr;

      // are we looking at body or headers:
      var content = response.body;
      if (spec.header) {
        content = response.headers;
      }

      parser(content, function(err, doc) {
        if (err) {
          // TODO: The message could be improved still
          if (spec.strict) {
            return next(new Error(`Couldn't parse response for '${expr}'`), null);
          }
        }

        let extractedValue = extractor(doc, template(expr, context), spec);

        if (spec.value) {
          // this is a match spec
          let expected = template(spec.value, context);
          debug('match: %s, expected: %s, got: %s', expr, expected, extractedValue);
          if (extractedValue !== expected) {
            result.matches[expr] = {
              success: false,
              expected: expected,
              got: extractedValue,
              expression: expr,
              strict: spec.strict
            };
          } else {
            result.matches.expr = {
              success: true,
              expected: expected,
              expression: expr
            };
          }
          return next(null);
        }

        if (spec.as) {
          // this is a capture
          debug('capture: %s = %s', spec.as, extractedValue);
          result.captures[spec.as] = extractedValue;
          if (spec.transform) {
            let transformedValue = evil(
              result.captures,
              spec.transform);

            debug('transform: %s = %s', spec.as, result.captures[spec.as]);
            result.captures[spec.as] = transformedValue !== null ? transformedValue : extractedValue;
          }
        }

        return next(null);
      });
    },
    function(err) {
      if (err) {
        return done(err, null);
      } else {
        return done(null, result);
      }
    });
}

function parseSpec(spec, response) {
  let parser;
  let extractor;
  let expr;

  if (spec.json) {
    parser = parseJSON;
    extractor = extractJSONPath;
    expr = spec.json;
  } else if (xmlCapture && spec.xpath) {
    parser = xmlCapture.parseXML;
    extractor = xmlCapture.extractXPath;
    expr = spec.xpath;
  } else if (spec.regexp) {
    parser = dummyParser;
    extractor = extractRegExp;
    expr = spec.regexp;
  } else if (spec.header) {
    parser = dummyParser;
    extractor = extractHeader;
    expr = spec.header;
  } else if (spec.selector) {
    parser = dummyParser;
    extractor = extractCheerio;
    expr = spec.selector;
  } else {
    if (isJSON(response)) {
      parser = parseJSON;
      extractor = extractJSONPath;
      expr = spec.json;
    } else if (xmlCapture && isXML(response)) {
      parser = xmlCapture.parseXML;
      extractor = xmlCapture.extractXPath;
      expr = spec.xpath;
    } else {
      // We really don't know what to do here.
      parser = dummyParser;
      extractor = dummyExtractor;
      expr = '';
    }
  }

  return { parser: parser, extractor: extractor, expr: expr };
}

/*
 * Wrap JSON.parse in a callback
 */
function parseJSON(body, callback) {
  let r = null;
  let err = null;

  try {
    if (typeof body === 'string') {
      r = JSON.parse(body);
    } else {
      r = body;
    }
  } catch(e) {
    err = e;
  }

  return callback(err, r);
}

function dummyParser(body, callback) {
  return callback(null, body);
}

// doc is a JSON object
function extractJSONPath(doc, expr) {
  let results = jsonpath.eval(doc, expr);

  if (!results) {
    return '';
  }

  if (results.length > 1) {
    return results[randomInt(0, results.length - 1)];
  } else {
    return results[0];
  }
}

// doc is a string or an object (body parsed by Request when headers indicate JSON)
function extractRegExp(doc, expr, opts) {
  let group = opts.group;
  let flags = opts.flags;
  let str;
  if (typeof doc === 'string') {
    str = doc;
  } else {
    str = JSON.stringify(doc); // FIXME: not the same string as the one we got from the server
  }
  let rx;
  if (flags) {
      rx = new RegExp(expr, flags);
  } else {
      rx = new RegExp(expr);
  }
  let match = rx.exec(str);
  if (!match) {
    return '';
  }
  if(group && match[group]) {
    return match[group];
  } else if (match[0]) {
    return match[0];
  } else {
    return '';
  }
}

function extractHeader(headers, headerName) {
  return headers[headerName] || '';
}

function extractCheerio(doc, expr, opts) {
  let $ = cheerio.load(doc);
  let els = $(expr);
  let i = 0;
  if (typeof opts.index !== 'undefined') {
    if (opts.index === 'random') {
      i = Math.ceil(Math.random() * els.get().length - 1);
    } else if (opts.index === 'last') {
      i = els.get().length() - 1;
    } else if (typeof Number(opts.index) === 'number') {
      i = Number(opts.index);
    }
  }
  return els.slice(i, i + 1).attr(opts.attr);
}

function dummyExtractor() {
  return '';
}

/*
 * Given a response object determine if it's JSON
 */
function isJSON(res) {
  debug('isJSON: content-type = %s', res.headers['content-type']);
  return (res.headers['content-type'] &&
          /^application\/json/.test(res.headers['content-type']));
}

/*
 * Given a response object determine if it's some kind of XML
 */
function isXML(res) {
  return (res.headers['content-type'] &&
          (/^[a-zA-Z]+\/xml/.test(res.headers['content-type']) ||
           /^[a-zA-Z]+\/[a-zA-Z]+\+xml/.test(res.headers['content-type'])));

}

function randomInt (low, high) {
  return Math.floor(Math.random() * (high - low + 1) + low);
}
<|endoftext|>


<commit_before>var fs = require('fs');
var https = require('https');
var Cron = require('cron').CronJob;

function getTemp() {
    fs.readFile('/sys/bus/w1/devices/28-00000400a88a/w1_slave', function (err, buffer) {
        if (err) {
            console.error(err);
            process.exit(1);
        }

        // Read data from file (using fast node ASCII encoding).
        var data = buffer.toString('ascii').split(" "); // Split by space

        // Extract temperature from string and divide by 1000 to give celsius
        var temp = parseFloat(data[data.length - 1].split("=")[1]) / 1000.0;

        // Round to one decimal place
        temp = Math.round(temp * 10) / 10;

        // Add date/time to temperature
        var response = {
            unix_time: Date.now(),
            c: temp,
            f: (temp * 9 / 5 + 32)
        };

        // Execute call back with data
        return response;
    });
};

var tempcheck = function () {
    console.log("client.js - tempcheck - start");
    var temp = getTemp();

    console.log(JSON.stringify(temp));

    var options = {
        host: 'https://repkam09.com',
        path: '/repserv/tempmon/' + temp.f
    };

    var req = https.get(options, function (res) {
        console.log('STATUS: ' + res.statusCode);

        var bodyChunks = [];
        res.on('data', function (chunk) {
            bodyChunks.push(chunk);
        });

        res.on('end', function () {
            var body = Buffer.concat(bodyChunks);
            console.log('Server Response: ' + body);
        });

    });

    req.on('error', function (e) {
        console.log('Server Error: ' + e.message);
    });

};

var onFinish = function () {
    console.log("client.js - onFinish - start");
};

// Start a new cron task
var task = new Cron('0 */2 * * *', tempcheck, onFinish, true, null, null);<commit_msg>Totally done<commit_after>var fs = require('fs');
var https = require('https');
var Cron = require('cron').CronJob;

function getTemp() {
    var buffer = fs.readFileSync('/sys/bus/w1/devices/28-000006b4a69b/w1_slave');

	// Read data from file (using fast node ASCII encoding).
        var data = buffer.toString('ascii').split(" "); // Split by space

        // Extract temperature from string and divide by 1000 to give celsius
        var temp = parseFloat(data[data.length - 1].split("=")[1]) / 1000.0;

        // Round to one decimal place
        temp = Math.round(temp * 10) / 10;

        // Execute call back with data
        return (temp * 9/5 + 32);
};

var tempcheck = function () {
    console.log("client.js - tempcheck - start");
    var temp = getTemp();

    console.log("Temp Returned: " + JSON.stringify(temp));

    var urlstring = "https://repkam09.com/repserv/tempmon/" + temp;

    var req = https.get(urlstring, function (res) {
        console.log('STATUS: ' + res.statusCode);

        var bodyChunks = [];
        res.on('data', function (chunk) {
            bodyChunks.push(chunk);
        });

        res.on('end', function () {
            var body = Buffer.concat(bodyChunks);
            console.log('Server Response: ' + body);
        });

    });

    req.on('error', function (e) {
        console.log('Server Error: ' + e.message + JSON.stringify(e));
    });

};

var onFinish = function () {
    console.log("client.js - onFinish - start");
};

// Start a new cron task
// var task = new Cron('0 */2 * * *', tempcheck, onFinish, true, null, null);

tempcheck();
<|endoftext|>


<commit_before>(function($) {
	$.fn.miller = function(settings) {
		var settings = $.extend({
					'url': function(id) { return id; },
					'minWidth': 40,
					'panel': {
						'width': 100,
						'options': {}
					}
				},
				settings
			)
		;

		this.addClass('miller');

		var path = $('<div>', { class: 'path' })
			.appendTo(this)
		;

		var columns = $('<div>', { class: 'columns' })
			.appendTo(this)
		;

		var getColumnsWidth = function() {
				var width = 0;

				columns.children().each(function() {
						width += $(this).width();
					}
				);

				return width;
			}
		;

		var removeNextColumns = function() {
				var line = $(this);

				var column = line.parent();

				column
					.nextAll()
						.slice(1)
							.remove()
				;

				column
					.find('li')
						.removeClass('selected parentSelected')
				;

				line.addClass(line.hasClass('parent') ? 'parentSelected' : 'selected');

				var columnIndex = column.index();

				columnIndex -= (columnIndex - (columnIndex / 2));

				path
					.append($('<span>', { text: line.text() }))
					.children()
						.slice(columnIndex, -1)
							.remove()
				;

			}
		;

		var buildColumn = function(lines) {
				if (lines == null) {
					$('li.parentLoading').remove();
				} else if (lines.length <= 0) {
					var line = $('li.parentLoading')
						.removeClass('parent')
						.addClass('selected')
					;

					var width = getColumnsWidth();

					var panel = $('<ul>')
						.css({ 'top': 0, 'left': width })
						.addClass('panel')
					;

					id = line.data('id');

					$.each(settings['panel']['options'], function(key, callbackGenerator) {
							var option = $('<li>', { text: key })
								.on('click', callbackGenerator(id))
							;

							option.appendTo(panel);
						}
					);

					columns
						.append(panel)
						.scrollLeft(width + panel.width())
					;
				} else {
					$('li.parentLoading').addClass('parentSelected');

					var width = getColumnsWidth();

					var column = $('<ul>')
						.css({ 'top': 0, 'left': width })
					;

					$.each(lines, function(id, data) {
							var line = $('<li>', { text: data['name'] })
								.data('id', data['id'])
								.on('click', removeNextColumns)
								.on('click', getLines)
								.appendTo(column)
							;

							if (data['parent']) {
								line.addClass('parent');
							}
						}
					);

					columns
						.append(column)
						.scrollLeft(width += column.width())
						.append(
							$('<div>', { class: 'grip' })
								.css({ 'top': 0, 'left': width })
								.mousedown(function(event) {
										var x = event.pageX;
										var cursor = columns.css('cursor');

										columns
											.css('cursor', 'col-resize')
											.mousemove(function(event) {
													var delta = event.pageX - x;
													var newWidth = column.width() + delta;

													if (newWidth > settings['minWidth']) {
														column
															.width(newWidth)
															.nextAll()
																.each(function() {
																		$(this).css('left', $(this).position().left + delta + columns.scrollLeft());
																	}
																)
														;
													}

													x = event.pageX;
												}
											)
											.mouseup(function() {
													columns
														.off('mousemove')
														.css('cursor', cursor)
													;
												}
											)
										;
									}
								)
						)
					;
				}
			}
		;

		var getLines = function(event) {
				var currentLine = $(event.currentTarget);

				currentLine
					.removeClass('parentSelected')
					.addClass('parentLoading')
				;

				$.getJSON(settings['url']($(this).data('id')), buildColumn)
					.always(function() {
							currentLine
								.removeClass('parentLoading')
							;
						}
					)
					.fail(function() {})
				;

			}
		;

		$.getJSON(settings['url'](), buildColumn);

		return this;
	};
})(jQuery);
<commit_msg>It's now possible to click in the path to go back in the tree.<commit_after>(function($) {
	$.fn.miller = function(settings) {
		var settings = $.extend({
					'url': function(id) { return id; },
					'minWidth': 40,
					'panel': {
						'width': 100,
						'options': {}
					}
				},
				settings
			)
		;

		this.addClass('miller');

		var path = $('<div>', { class: 'path' })
			.appendTo(this)
		;

		var columns = $('<div>', { class: 'columns' })
			.appendTo(this)
		;

		var getColumnsWidth = function() {
				var width = 0;

				columns.children().each(function() {
						width += $(this).width();
					}
				);

				return width;
			}
		;

		var removeNextColumns = function() {
				var line = $(this);

				var column = line.parent();

				column
					.nextAll()
						.slice(1)
							.remove()
				;

				column
					.find('li')
						.removeClass('selected parentSelected')
				;

				line.addClass(line.hasClass('parent') ? 'parentSelected' : 'selected');

				var columnIndex = column.index();

				columnIndex -= (columnIndex - (columnIndex / 2));

				path
					.append($('<span>', { text: line.text(), css: { cursor: 'pointer' } })
						.on('click', function() {
								columns
									.children()
										.slice((($(this).index() * 2) + 4))
											.remove()
								;
								columns
									.children('ul:last')
											.find('li')
												.removeClass('parentSelected')
								;
								path
									.children()
										.slice($(this).index() + 1)
											.remove()
								;
							}
						)
					)
					.children()
						.slice(columnIndex, -1)
							.remove()
				;

			}
		;

		var buildColumn = function(lines) {
				if (lines == null) {
					$('li.parentLoading').remove();
				} else if (lines.length <= 0) {
					var line = $('li.parentLoading')
						.removeClass('parent')
						.addClass('selected')
					;

					var width = getColumnsWidth();

					var panel = $('<ul>')
						.css({ 'top': 0, 'left': width })
						.addClass('panel')
					;

					id = line.data('id');

					$.each(settings['panel']['options'], function(key, callbackGenerator) {
							var option = $('<li>', { text: key })
								.on('click', callbackGenerator(id))
							;

							option.appendTo(panel);
						}
					);

					columns
						.append(panel)
						.scrollLeft(width + panel.width())
					;
				} else {
					$('li.parentLoading').addClass('parentSelected');

					var width = getColumnsWidth();

					var column = $('<ul>')
						.css({ 'top': 0, 'left': width })
					;

					$.each(lines, function(id, data) {
							var line = $('<li>', { text: data['name'] })
								.data('id', data['id'])
								.on('click', removeNextColumns)
								.on('click', getLines)
								.appendTo(column)
							;

							if (data['parent']) {
								line.addClass('parent');
							}
						}
					);

					columns
						.append(column)
						.scrollLeft(width += column.width())
						.append(
							$('<div>', { class: 'grip' })
								.css({ 'top': 0, 'left': width })
								.mousedown(function(event) {
										var x = event.pageX;
										var cursor = columns.css('cursor');

										columns
											.css('cursor', 'col-resize')
											.mousemove(function(event) {
													var delta = event.pageX - x;
													var newWidth = column.width() + delta;

													if (newWidth > settings['minWidth']) {
														column
															.width(newWidth)
															.nextAll()
																.each(function() {
																		$(this).css('left', $(this).position().left + delta + columns.scrollLeft());
																	}
																)
														;
													}

													x = event.pageX;
												}
											)
											.mouseup(function() {
													columns
														.off('mousemove')
														.css('cursor', cursor)
													;
												}
											)
										;
									}
								)
						)
					;
				}
			}
		;

		var getLines = function(event) {
				var currentLine = $(event.currentTarget);

				currentLine
					.removeClass('parentSelected')
					.addClass('parentLoading')
				;

				$.getJSON(settings['url']($(this).data('id')), buildColumn)
					.always(function() {
							currentLine
								.removeClass('parentLoading')
							;
						}
					)
					.fail(function() {})
				;

			}
		;

		$.getJSON(settings['url'](), buildColumn);

		return this;
	};
})(jQuery);
<|endoftext|>


<commit_before>/**
* el.js v0.3 - A JavaScript Node Creation Tool
*
* https://github.com/markgandolfo/el.js
*
* Copyright 2013 Mark Gandolfo and other contributors
* Released under the MIT license.
* http://en.wikipedia.org/wiki/MIT_License
*/
window.el=function(){var a=function(d,b){b="undefined"!==typeof b?b:{};matched=d.match(/([a-z]+|#[\w-\d]+|\.[\w\d-]+)/g);d=matched[0];matched.shift();for(var c in matched)"."==matched[c][0]?b["class"]=void 0==b["class"]?matched[c].substring(1,matched[c].length):b["class"]+" "+matched[c].substring(1,matched[c].length):"#"==matched[c][0]&&(b.id=void 0==b.id?matched[c].substring(1,matched[c].length):b.id+" "+matched[c].substring(1,matched[c].length));c=document.createElement(d);b.content&&("object"==
typeof b.content?c.appendChild(b.content):c.innerHTML=b.content,delete b.content);for(var a in b)b.hasOwnProperty(a)&&c.setAttribute(a,b[a]);return c};a.create=a.c=a;a.img=function(a){return this.create("img",a)};a.a=function(a){return this.create("a",a)};a.div=function(a){return this.create("div",a)};a.p=function(a){return this.create("p",a)};a.input=function(a){return this.create("input",a)};return a}();<commit_msg>removed minified version as it was consistently out of date<commit_after><|endoftext|>


<commit_before>// utils for Impl

var UtilImpl = {
  DomEvt: _class("Impl.UtilImpl.DomEvt", {

    class_methods: {
      addEvt: ((_window && (_window.document.addEventListener)) ?
               function(elem, type, func){ return elem.addEventListener(type, func, false); } :
               ((_window && (_window.document.attachEvent)) ?
                function(elem, type, func) { return elem.attachEvent('on' + type, func); } :
                function() { throw new NotSupported("This Browser is not Supported add Event to a DomElement."); })),

      remEvt: ((_window && (_window.document.removeEventListener)) ?
               function(elem, type, func) { return elem.removeEventListener(type, func, false); } :
               ((_window && (_window.document.detachEvent)) ?
                 function(elem, tyoe, func) { return elem.detachEvent('on' + type, func); } :
                function() { throw new NotSupported("This Browser is not Supported add Event to a DomElement."); }))
    },

    methods: {
      convertToMouseEvt: function convertToMouseEvt(dom_evt) {
        var which = dom_evt.which;
        var button = dom_evt.button;
        if (!which && button !== undefined ) {
          which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) );
        }
        switch(which) {
        case 0: this.left = this.middle = this.right = false; break;
        case 1: this.left = true; break;
        case 2: this.middle = true; break;
        case 3: this.right = true; break;
        }

        var pageX = dom_evt.pageX;
        var pageY = dom_evt.pageY;
        if ( dom_evt.pageX == null && dom_evt.clientX != null ) {
          var eventDoc = dom_evt.target.ownerDocument || document;
          var doc = eventDoc.documentElement;
          var body = eventDoc.body;

          pageX = dom_evt.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 );
          pageY = dom_evt.clientY + ( doc && doc.scrollTop  || body && body.scrollTop  || 0 ) - ( doc && doc.clientTop  || body && body.clientTop  || 0 );
        }

        this.pagePosition.x = pageX;
        this.pagePosition.y = pageY;

        return this;
      }
    }
  }),

  getDomOffsetPosition: (function () {

    var support_box_model = (document.compatMode === "CSS1Compat");

    var contains = (function() {
      if ( document.documentElement.contains ) {
        return function( a, b ) {
          return a !== b && (a.contains ? a.contains(b) : true);
        };

      } else if ( document.documentElement.compareDocumentPosition ) {
        return function( a, b ) {
          return !!(a.compareDocumentPosition(b) & 16);
        };

      } else {
        return function() {
          return false;
        };
      }
    })();

    if ( "getBoundingClientRect" in document.documentElement )
      return function getDomOffsetPosition_boundingClientRect(elem, doc, docElem, box ) {
        doc = document; docElem = document.documentElement;

        try {
          box = elem.getBoundingClientRect();
        } catch(e) {}

        if ( !box || !contains( docElem, elem ) ) {
          console.log('here');
          return box ? { top: box.top, left: box.left } : { top: 0, left: 0 };
        }

        var body = doc.body,
        clientTop  = docElem.clientTop  || body.clientTop  || 0,
        clientLeft = docElem.clientLeft || body.clientLeft || 0,
        scrollTop  = _window.pageYOffset || support_box_model && docElem.scrollTop  || body.scrollTop,
        scrollLeft = _window.pageXOffset || support_box_model && docElem.scrollLeft || body.scrollLeft,
        top  = box.top  + scrollTop  - clientTop,
        left = box.left + scrollLeft - clientLeft;

        return { x: left, y: top };
      };

    return function getDomOffsetPosition(elem) {
      var curleft = 0, curtop = 0;
      if (elem.offsetParent) {
        do {
          curleft += elem.offsetLeft;
          curtop += elem.offsetTop;

        } while (elem = elem.offsetParent);
      }
      return {x: curleft, y: curtop};
    };

  })()

};

<commit_msg>fix test.<commit_after>// utils for Impl

var UtilImpl = {
  DomEvt: _class("Impl.UtilImpl.DomEvt", {

    class_methods: {
      addEvt: ((_window && (_window.document.addEventListener)) ?
               function(elem, type, func){ return elem.addEventListener(type, func, false); } :
               ((_window && (_window.document.attachEvent)) ?
                function(elem, type, func) { return elem.attachEvent('on' + type, func); } :
                function() { throw new NotSupported("This Browser is not Supported add Event to a DomElement."); })),

      remEvt: ((_window && (_window.document.removeEventListener)) ?
               function(elem, type, func) { return elem.removeEventListener(type, func, false); } :
               ((_window && (_window.document.detachEvent)) ?
                 function(elem, tyoe, func) { return elem.detachEvent('on' + type, func); } :
                function() { throw new NotSupported("This Browser is not Supported add Event to a DomElement."); }))
    },

    methods: {
      convertToMouseEvt: function convertToMouseEvt(dom_evt) {
        var which = dom_evt.which;
        var button = dom_evt.button;
        if (!which && button !== undefined ) {
          which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) );
        }
        switch(which) {
        case 0: this.left = this.middle = this.right = false; break;
        case 1: this.left = true; break;
        case 2: this.middle = true; break;
        case 3: this.right = true; break;
        }

        var pageX = dom_evt.pageX;
        var pageY = dom_evt.pageY;
        if ( dom_evt.pageX == null && dom_evt.clientX != null ) {
          var eventDoc = dom_evt.target.ownerDocument || document;
          var doc = eventDoc.documentElement;
          var body = eventDoc.body;

          pageX = dom_evt.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 );
          pageY = dom_evt.clientY + ( doc && doc.scrollTop  || body && body.scrollTop  || 0 ) - ( doc && doc.clientTop  || body && body.clientTop  || 0 );
        }

        this.pagePosition.x = pageX;
        this.pagePosition.y = pageY;

        return this;
      }
    }
  }),

  getDomOffsetPosition: (function () {

    var support_box_model =  (_window && _window.document.compatMode === "CSS1Compat");

    var contains = (function() {
      if ( _window && _window.document.documentElement.contains ) {
        return function( a, b ) {
          return a !== b && (a.contains ? a.contains(b) : true);
        };

      } else if ( _window && _window.document.documentElement.compareDocumentPosition ) {
        return function( a, b ) {
          return !!(a.compareDocumentPosition(b) & 16);
        };

      } else {
        return function() {
          return false;
        };
      }
    })();

    if ( _window && "getBoundingClientRect" in _window.document.documentElement )
      return function getDomOffsetPosition_boundingClientRect(elem, doc, docElem, box ) {
        doc = document; docElem = document.documentElement;

        try {
          box = elem.getBoundingClientRect();
        } catch(e) {}

        if ( !box || !contains( docElem, elem ) ) {
          console.log('here');
          return box ? { top: box.top, left: box.left } : { top: 0, left: 0 };
        }

        var body = doc.body,
        clientTop  = docElem.clientTop  || body.clientTop  || 0,
        clientLeft = docElem.clientLeft || body.clientLeft || 0,
        scrollTop  = _window.pageYOffset || support_box_model && docElem.scrollTop  || body.scrollTop,
        scrollLeft = _window.pageXOffset || support_box_model && docElem.scrollLeft || body.scrollLeft,
        top  = box.top  + scrollTop  - clientTop,
        left = box.left + scrollLeft - clientLeft;

        return { x: left, y: top };
      };

    return function getDomOffsetPosition(elem) {
      var curleft = 0, curtop = 0;
      if (elem.offsetParent) {
        do {
          curleft += elem.offsetLeft;
          curtop += elem.offsetTop;

        } while (elem = elem.offsetParent);
      }
      return {x: curleft, y: curtop};
    };

  })()

};

<|endoftext|>


<commit_before>/*
 * Copyright (C) 2014 Vanderbilt University, All rights reserved.
 *
 * Author: Zsolt Lattmann
 */

'use strict';
define(['plugin/PluginConfig',
    'plugin/PluginResult',
    'plugin/PluginMessage',
    'plugin/PluginNodeDescription'],
    function (PluginConfig, PluginResult, PluginMessage, PluginNodeDescription) {

        /**
         * Initializes a new instance of a plugin object, which should be a derived class.
         *
         * @constructor
         */
        var PluginBase = function () {
            // set by initialize
            this.logger = null;
            this.blobClient = null;
            this._currentConfig = null;

            // set by configure
            this.core = null;
            this.project = null;
            this.projectName = null;
            this.branchName = null;
            this.branchHash = null;
            this.commitHash = null;
            this.currentHash = null;
            this.rootNode = null;
            this.activeNode = null;
            this.activeSelection = [];
            this.META = null;

            this.result = null;
            this.isConfigured = false;
        };

        //--------------------------------------------------------------------------------------------------------------
        //---------- Methods must be overridden by the derived classes

        /**
         * Main function for the plugin to execute. This will perform the execution.
         * Notes:
         * - do NOT use console.log use this.logger.[error,warning,info,debug] instead
         * - do NOT put any user interaction logic UI, etc. inside this function
         * - callback always have to be called even if error happened
         *
         * @param {function(string, plugin.PluginResult)} callback - the result callback
         */
        PluginBase.prototype.main = function (callback) {
            throw new Error('implement this function in the derived class');
        };

        /**
         * Readable name of this plugin that can contain spaces.
         *
         * @returns {string}
         */
        PluginBase.prototype.getName = function () {
            throw new Error('implement this function in the derived class - getting type automatically is a bad idea,' +
                'when the js scripts are minified names are useless.');
        };

        //--------------------------------------------------------------------------------------------------------------
        //---------- Methods could be overridden by the derived classes

        /**
         * Current version of this plugin using semantic versioning.
         * @returns {string}
         */
        PluginBase.prototype.getVersion = function () {
            return '0.1.0';
        };

        /**
         * A detailed description of this plugin and its purpose. It can be one or more sentences.
         *
         * @returns {string}
         */
        PluginBase.prototype.getDescription = function () {
            return '';
        };

        /**
         * Configuration structure with names, descriptions, minimum, maximum values, default values and
         * type definitions.
         *
         * Example:
         *
         * [{
         *    "name": "logChildrenNames",
         *    "displayName": "Log Children Names",
         *    "description": '',
         *    "value": true, // this is the 'default config'
         *    "valueType": "boolean",
         *    "readOnly": false
         * },{
         *    "name": "logLevel",
         *    "displayName": "Logger level",
         *    "description": '',
         *    "value": "info",
         *    "valueType": "string",
         *    "valueItems": [
         *          "debug",
         *          "info",
         *          "warn",
         *          "error"
         *      ],
         *    "readOnly": false
         * },{
         *    "name": "maxChildrenToLog",
         *    "displayName": "Maximum children to log",
         *    "description": 'Set this parameter to blabla',
         *    "value": 4,
         *    "minValue": 1,
         *    "valueType": "number",
         *    "readOnly": false
         * }]
         *
         * @returns {object[]}
         */
        PluginBase.prototype.getConfigStructure = function () {
            return [];
        };

        //--------------------------------------------------------------------------------------------------------------
        //---------- Methods that can be used by the derived classes

        /**
         * Updates the current success flag with a new value.
         *
         * NewValue = OldValue && Value
         *
         * @param {boolean} value - apply this flag on current success value
         * @param {string|null} message - optional detailed message
         */
        PluginBase.prototype.updateSuccess = function (value, message) {
            var prevSuccess = this.result.getSuccess();
            var newSuccessValue = prevSuccess && value;

            this.result.setSuccess(newSuccessValue);
            var msg = '';
            if (message) {
                msg = ' - ' + message;
            }

            this.logger.debug('Success was updated from ' + prevSuccess + ' to ' + newSuccessValue + msg);
        };

        /**
         * WebGME can export the META types as path and this method updates the generated domain specific types with
         * webgme node objects. These can be used to define the base class of new objects created through the webgme API.
         *
         * @param {object} generatedMETA
         */
        PluginBase.prototype.updateMETA = function (generatedMETA) {
            var name;
            for (name in this.META) {
                if (this.META.hasOwnProperty(name)) {
                    generatedMETA[name] = this.META[name];
                }
            }

            // TODO: check if names are not the same
            // TODO: log if META is out of date
        };

        /**
         * Finds and returns the node object defining the meta type for the given node.
         * @param node - Node to be checked for type.
         * @returns {Object} - Node object defining the meta type of node.
         */
        PluginBase.prototype.getMetaType = function (node) {
            var self = this,
                name;
            while (node) {
                name = self.core.getAttribute(node, 'name');
                if (self.META.hasOwnProperty(name) && self.META[name] === node) {
                    break;
                }
                node = self.core.getBase(node);
            }
            return node;
        };

        /**
         * Returns true if node is a direct instance of a meta-type node (or a meta-type node itself).
         * @param node - Node to be checked.
         * @returns {boolean}
         */
        PluginBase.prototype.baseIsMeta = function (node) {
            var self = this,
                baseName,
                baseNode = self.core.getBase(node);
            if (!baseNode) {
                // FCO does not have a base node, by definition function returns true.
                return true;
            }
            baseName = self.core.getAttribute(baseNode, 'name');
            return self.META.hasOwnProperty(baseName) && self.META[baseName] === baseNode;
        };

        /**
         * Gets the current configuration of the plugin that was set by the user and plugin manager.
         *
         * @returns {object}
         */
        PluginBase.prototype.getCurrentConfig = function () {
            return this._currentConfig;
        };

        /**
         * Creates a new message for the user and adds it to the result.
         *
         * @param {object} node - webgme object which is related to the message
         * @param {string} message - feedback to the user
         */
        PluginBase.prototype.createMessage = function (node, message) {
            //this occurence of the function will always handle a single node

            var descriptor = new PluginNodeDescription({
                    name: node ? this.core.getAttribute(node, 'name') : "",
                    id: node ? this.core.getPath(node) : ""
                });
            var pluginMessage = new PluginMessage({
                    commitHash: this.currentHash,
                    activeNode: descriptor,
                    message: message
                });

            this.result.addMessage(pluginMessage);
        };

        /**
         * Saves all current changes if there is any to a new commit.
         * If the changes were started from a branch, then tries to fast forward the branch to the new commit.
         * Note: Does NOT handle any merges at this point.
         *
         * @param {string|null} message - commit message
         * @param callback
         */
        PluginBase.prototype.save = function (message, callback) {
            var self = this;

            this.logger.debug('Saving project');

            // Commit changes.
            this.core.persist(this.rootNode, function (err) {
                // TODO: any error here?
                if (err) {
                    self.logger.error(err);
                }
            });

            var newRootHash = this.core.getHash(this.rootNode);

            var commitMessage = '[Plugin] ' + this.getName() + ' (v' + this.getVersion() + ') updated the model.';
            if (message) {
                commitMessage += ' - ' + message;
            }

            this.currentHash = this.project.makeCommit([this.currentHash], newRootHash, commitMessage, function (err) {
                // TODO: any error handling here?
                if (err) {
                    self.logger.error(err);
                }
            });

            if (this.branchName) {
                // try to fast forward branch if there was a branch name defined

                // FIXME: what if master branch is already in a different state?

                this.project.getBranchNames(function (err, branchNames) {
                    if (branchNames.hasOwnProperty(self.branchName)) {
                        var branchHash = branchNames[self.branchName];
                        if (branchHash === self.branchHash) {
                            // the branch does not have any new commits
                            // try to fast forward branch to the current commit
                            self.project.setBranchHash(self.branchName, self.branchHash, self.currentHash, function (err) {
                                if (err) {
                                    // fast forward failed
                                    self.logger.error(err);
                                    self.logger.info('"' + self.branchName + '" was NOT updated');
                                    self.logger.info('Project was saved to ' + self.currentHash + ' commit.');
                                } else {
                                    // successful fast forward of branch to the new commit
                                    self.logger.info('"' + self.branchName + '" was updated to the new commit.');
                                    // roll starting point on success
                                    self.branchHash = self.currentHash;
                                }
                                callback(err);
                            });
                        } else {
                            // branch has changes a merge is required
                            // TODO: try auto-merge, if fails ...
                            self.logger.warn('Cannot fast forward "' + self.branchName + '" branch. Merge is required but not supported yet.');
                            self.logger.info('Project was saved to ' + self.currentHash + ' commit.');
                            callback(null);
                        }
                    } else {
                        // branch was deleted or not found, do nothing
                        self.logger.info('Project was saved to ' + self.currentHash + ' commit.');
                        callback(null);
                    }
                });
                // FIXME: is this call async??
                // FIXME: we are not tracking all commits that we make

            } else {
                // making commits, we have not started from a branch
                this.logger.info('Project was saved to ' + this.currentHash + ' commit.');
                callback(null);
            }
        };

        //--------------------------------------------------------------------------------------------------------------
        //---------- Methods that are used by the Plugin Manager. Derived classes should not use these methods

        /**
         * Initializes the plugin with objects that can be reused within the same plugin instance.
         *
         * @param {logManager} logger - logging capability to console (or file) based on PluginManager configuration
         * @param {blob.BlobClient} blobClient - virtual file system where files can be generated then saved as a zip file.
         */
        PluginBase.prototype.initialize = function (logger, blobClient) {
            if (logger) {
                this.logger = logger;
            } else {
                this.logger = console;
            }

            this.blobClient = blobClient;

            this._currentConfig = null;
            // initialize default configuration
            this.setCurrentConfig(this.getDefaultConfig());

            this.isConfigured = false;
        };

        /**
         * Configures this instance of the plugin for a specific execution. This function is called before the main by
         * the PluginManager.
         * Initializes the result with a new object.
         *
         * @param {PluginContext} config - specific context: project, branch, core, active object and active selection.
         */
        PluginBase.prototype.configure = function (config) {
            this.core = config.core;
            this.project = config.project;
            this.projectName = config.projectName;
            this.branchName = config.branchName;
            this.branchHash = config.branchName ? config.commitHash : null;
            this.commitHash = config.commitHash;
            this.currentHash = config.commitHash;
            this.rootNode = config.rootNode;
            this.activeNode = config.activeNode;
            this.activeSelection = config.activeSelection;
            this.META = config.META;

            this.result = new PluginResult();

            this.isConfigured = true;
        };

        /**
         * Gets the default configuration based on the configuration structure for this plugin.
         *
         * @returns {plugin.PluginConfig}
         */
        PluginBase.prototype.getDefaultConfig = function () {
            var configStructure = this.getConfigStructure();

            var defaultConfig = new PluginConfig();

            for (var i = 0; i < configStructure.length; i += 1) {
                defaultConfig[configStructure[i].name] = configStructure[i].value;
            }

            return defaultConfig;
        };

        /**
         * Sets the current configuration of the plugin.
         *
         * @param {object} newConfig - this is the actual configuration and NOT the configuration structure.
         */
        PluginBase.prototype.setCurrentConfig = function (newConfig) {
            this._currentConfig = newConfig;
        };

        return PluginBase;
    });<commit_msg>Add isMetaTypeOf helper function to pluginBase<commit_after>/*
 * Copyright (C) 2014 Vanderbilt University, All rights reserved.
 *
 * Author: Zsolt Lattmann
 */

'use strict';
define(['plugin/PluginConfig',
    'plugin/PluginResult',
    'plugin/PluginMessage',
    'plugin/PluginNodeDescription'],
    function (PluginConfig, PluginResult, PluginMessage, PluginNodeDescription) {

        /**
         * Initializes a new instance of a plugin object, which should be a derived class.
         *
         * @constructor
         */
        var PluginBase = function () {
            // set by initialize
            this.logger = null;
            this.blobClient = null;
            this._currentConfig = null;

            // set by configure
            this.core = null;
            this.project = null;
            this.projectName = null;
            this.branchName = null;
            this.branchHash = null;
            this.commitHash = null;
            this.currentHash = null;
            this.rootNode = null;
            this.activeNode = null;
            this.activeSelection = [];
            this.META = null;

            this.result = null;
            this.isConfigured = false;
        };

        //--------------------------------------------------------------------------------------------------------------
        //---------- Methods must be overridden by the derived classes

        /**
         * Main function for the plugin to execute. This will perform the execution.
         * Notes:
         * - do NOT use console.log use this.logger.[error,warning,info,debug] instead
         * - do NOT put any user interaction logic UI, etc. inside this function
         * - callback always have to be called even if error happened
         *
         * @param {function(string, plugin.PluginResult)} callback - the result callback
         */
        PluginBase.prototype.main = function (callback) {
            throw new Error('implement this function in the derived class');
        };

        /**
         * Readable name of this plugin that can contain spaces.
         *
         * @returns {string}
         */
        PluginBase.prototype.getName = function () {
            throw new Error('implement this function in the derived class - getting type automatically is a bad idea,' +
                'when the js scripts are minified names are useless.');
        };

        //--------------------------------------------------------------------------------------------------------------
        //---------- Methods could be overridden by the derived classes

        /**
         * Current version of this plugin using semantic versioning.
         * @returns {string}
         */
        PluginBase.prototype.getVersion = function () {
            return '0.1.0';
        };

        /**
         * A detailed description of this plugin and its purpose. It can be one or more sentences.
         *
         * @returns {string}
         */
        PluginBase.prototype.getDescription = function () {
            return '';
        };

        /**
         * Configuration structure with names, descriptions, minimum, maximum values, default values and
         * type definitions.
         *
         * Example:
         *
         * [{
         *    "name": "logChildrenNames",
         *    "displayName": "Log Children Names",
         *    "description": '',
         *    "value": true, // this is the 'default config'
         *    "valueType": "boolean",
         *    "readOnly": false
         * },{
         *    "name": "logLevel",
         *    "displayName": "Logger level",
         *    "description": '',
         *    "value": "info",
         *    "valueType": "string",
         *    "valueItems": [
         *          "debug",
         *          "info",
         *          "warn",
         *          "error"
         *      ],
         *    "readOnly": false
         * },{
         *    "name": "maxChildrenToLog",
         *    "displayName": "Maximum children to log",
         *    "description": 'Set this parameter to blabla',
         *    "value": 4,
         *    "minValue": 1,
         *    "valueType": "number",
         *    "readOnly": false
         * }]
         *
         * @returns {object[]}
         */
        PluginBase.prototype.getConfigStructure = function () {
            return [];
        };

        //--------------------------------------------------------------------------------------------------------------
        //---------- Methods that can be used by the derived classes

        /**
         * Updates the current success flag with a new value.
         *
         * NewValue = OldValue && Value
         *
         * @param {boolean} value - apply this flag on current success value
         * @param {string|null} message - optional detailed message
         */
        PluginBase.prototype.updateSuccess = function (value, message) {
            var prevSuccess = this.result.getSuccess();
            var newSuccessValue = prevSuccess && value;

            this.result.setSuccess(newSuccessValue);
            var msg = '';
            if (message) {
                msg = ' - ' + message;
            }

            this.logger.debug('Success was updated from ' + prevSuccess + ' to ' + newSuccessValue + msg);
        };

        /**
         * WebGME can export the META types as path and this method updates the generated domain specific types with
         * webgme node objects. These can be used to define the base class of new objects created through the webgme API.
         *
         * @param {object} generatedMETA
         */
        PluginBase.prototype.updateMETA = function (generatedMETA) {
            var name;
            for (name in this.META) {
                if (this.META.hasOwnProperty(name)) {
                    generatedMETA[name] = this.META[name];
                }
            }

            // TODO: check if names are not the same
            // TODO: log if META is out of date
        };

        /**
         * Checks if the given node is of the given meta-type.
         * Usage: <tt>self.isMetaTypeOf(aNode, self.META['FCO']);</tt>
         * @param node - Node to be checked for type.
         * @param metaNode - Node object defining the meta type.
         * @returns {boolean} - True if the given object was of the META type.
         */
        PluginBase.prototype.isMetaTypeOf = function (node, metaNode) {
            var self = this;
            while (node) {
                if (node === metaNode) {
                    return true;
                }
                node = self.core.getBase(node);
            }
            return false;
        };

        /**
         * Finds and returns the node object defining the meta type for the given node.
         * @param node - Node to be checked for type.
         * @returns {Object} - Node object defining the meta type of node.
         */
        PluginBase.prototype.getMetaType = function (node) {
            var self = this,
                name;
            while (node) {
                name = self.core.getAttribute(node, 'name');
                if (self.META.hasOwnProperty(name) && self.META[name] === node) {
                    break;
                }
                node = self.core.getBase(node);
            }
            return node;
        };

        /**
         * Returns true if node is a direct instance of a meta-type node (or a meta-type node itself).
         * @param node - Node to be checked.
         * @returns {boolean}
         */
        PluginBase.prototype.baseIsMeta = function (node) {
            var self = this,
                baseName,
                baseNode = self.core.getBase(node);
            if (!baseNode) {
                // FCO does not have a base node, by definition function returns true.
                return true;
            }
            baseName = self.core.getAttribute(baseNode, 'name');
            return self.META.hasOwnProperty(baseName) && self.META[baseName] === baseNode;
        };

        /**
         * Gets the current configuration of the plugin that was set by the user and plugin manager.
         *
         * @returns {object}
         */
        PluginBase.prototype.getCurrentConfig = function () {
            return this._currentConfig;
        };

        /**
         * Creates a new message for the user and adds it to the result.
         *
         * @param {object} node - webgme object which is related to the message
         * @param {string} message - feedback to the user
         */
        PluginBase.prototype.createMessage = function (node, message) {
            //this occurence of the function will always handle a single node

            var descriptor = new PluginNodeDescription({
                    name: node ? this.core.getAttribute(node, 'name') : "",
                    id: node ? this.core.getPath(node) : ""
                });
            var pluginMessage = new PluginMessage({
                    commitHash: this.currentHash,
                    activeNode: descriptor,
                    message: message
                });

            this.result.addMessage(pluginMessage);
        };

        /**
         * Saves all current changes if there is any to a new commit.
         * If the changes were started from a branch, then tries to fast forward the branch to the new commit.
         * Note: Does NOT handle any merges at this point.
         *
         * @param {string|null} message - commit message
         * @param callback
         */
        PluginBase.prototype.save = function (message, callback) {
            var self = this;

            this.logger.debug('Saving project');

            // Commit changes.
            this.core.persist(this.rootNode, function (err) {
                // TODO: any error here?
                if (err) {
                    self.logger.error(err);
                }
            });

            var newRootHash = this.core.getHash(this.rootNode);

            var commitMessage = '[Plugin] ' + this.getName() + ' (v' + this.getVersion() + ') updated the model.';
            if (message) {
                commitMessage += ' - ' + message;
            }

            this.currentHash = this.project.makeCommit([this.currentHash], newRootHash, commitMessage, function (err) {
                // TODO: any error handling here?
                if (err) {
                    self.logger.error(err);
                }
            });

            if (this.branchName) {
                // try to fast forward branch if there was a branch name defined

                // FIXME: what if master branch is already in a different state?

                this.project.getBranchNames(function (err, branchNames) {
                    if (branchNames.hasOwnProperty(self.branchName)) {
                        var branchHash = branchNames[self.branchName];
                        if (branchHash === self.branchHash) {
                            // the branch does not have any new commits
                            // try to fast forward branch to the current commit
                            self.project.setBranchHash(self.branchName, self.branchHash, self.currentHash, function (err) {
                                if (err) {
                                    // fast forward failed
                                    self.logger.error(err);
                                    self.logger.info('"' + self.branchName + '" was NOT updated');
                                    self.logger.info('Project was saved to ' + self.currentHash + ' commit.');
                                } else {
                                    // successful fast forward of branch to the new commit
                                    self.logger.info('"' + self.branchName + '" was updated to the new commit.');
                                    // roll starting point on success
                                    self.branchHash = self.currentHash;
                                }
                                callback(err);
                            });
                        } else {
                            // branch has changes a merge is required
                            // TODO: try auto-merge, if fails ...
                            self.logger.warn('Cannot fast forward "' + self.branchName + '" branch. Merge is required but not supported yet.');
                            self.logger.info('Project was saved to ' + self.currentHash + ' commit.');
                            callback(null);
                        }
                    } else {
                        // branch was deleted or not found, do nothing
                        self.logger.info('Project was saved to ' + self.currentHash + ' commit.');
                        callback(null);
                    }
                });
                // FIXME: is this call async??
                // FIXME: we are not tracking all commits that we make

            } else {
                // making commits, we have not started from a branch
                this.logger.info('Project was saved to ' + this.currentHash + ' commit.');
                callback(null);
            }
        };

        //--------------------------------------------------------------------------------------------------------------
        //---------- Methods that are used by the Plugin Manager. Derived classes should not use these methods

        /**
         * Initializes the plugin with objects that can be reused within the same plugin instance.
         *
         * @param {logManager} logger - logging capability to console (or file) based on PluginManager configuration
         * @param {blob.BlobClient} blobClient - virtual file system where files can be generated then saved as a zip file.
         */
        PluginBase.prototype.initialize = function (logger, blobClient) {
            if (logger) {
                this.logger = logger;
            } else {
                this.logger = console;
            }

            this.blobClient = blobClient;

            this._currentConfig = null;
            // initialize default configuration
            this.setCurrentConfig(this.getDefaultConfig());

            this.isConfigured = false;
        };

        /**
         * Configures this instance of the plugin for a specific execution. This function is called before the main by
         * the PluginManager.
         * Initializes the result with a new object.
         *
         * @param {PluginContext} config - specific context: project, branch, core, active object and active selection.
         */
        PluginBase.prototype.configure = function (config) {
            this.core = config.core;
            this.project = config.project;
            this.projectName = config.projectName;
            this.branchName = config.branchName;
            this.branchHash = config.branchName ? config.commitHash : null;
            this.commitHash = config.commitHash;
            this.currentHash = config.commitHash;
            this.rootNode = config.rootNode;
            this.activeNode = config.activeNode;
            this.activeSelection = config.activeSelection;
            this.META = config.META;

            this.result = new PluginResult();

            this.isConfigured = true;
        };

        /**
         * Gets the default configuration based on the configuration structure for this plugin.
         *
         * @returns {plugin.PluginConfig}
         */
        PluginBase.prototype.getDefaultConfig = function () {
            var configStructure = this.getConfigStructure();

            var defaultConfig = new PluginConfig();

            for (var i = 0; i < configStructure.length; i += 1) {
                defaultConfig[configStructure[i].name] = configStructure[i].value;
            }

            return defaultConfig;
        };

        /**
         * Sets the current configuration of the plugin.
         *
         * @param {object} newConfig - this is the actual configuration and NOT the configuration structure.
         */
        PluginBase.prototype.setCurrentConfig = function (newConfig) {
            this._currentConfig = newConfig;
        };

        return PluginBase;
    });<|endoftext|>


<commit_before>import { computed } from '@ember/object';
import { inject as service } from '@ember/service';
import { Ability } from 'ember-can';
import { w } from '@ember/string';

export default Ability.extend({
  currentUser: service(),

  canViewHealth: computed('currentUser.role_id', function () {
    switch (this.get('currentUser.role_id')) {
      case 'staff_admin':
      case 'provider_admin':
        return true;
      default:
        return false;
    }
  }),
  canViewState: computed('currentUser.role_id', function () {
    switch (this.get('currentUser.role_id')) {
      case 'staff_admin':
      case 'provider_admin':
      case 'client_admin':
        return true;
      default:
        return false;
    }
  }),
  canSource: computed('currentUser.role_id', function () {
    switch (this.get('currentUser.role_id')) {
      case 'staff_admin':
        return true;
      default:
        return false;
    }
  }),
  canTransfer: computed('currentUser.role_id', 'currentUser.provider_id', 'model.repository.provider.id', 'repository.provider.id', 'model.repository.id', 'model.query.repository-id', function () {
    switch (this.get('currentUser.role_id')) {
      case 'staff_admin':
        if (w("crossref.citations medra.citations kisti.citations jalc.citations op.citations").includes(this.get('model.repository.id')) || w("crossref.citations medra.citations kisti.citations jalc.citations op.citations").includes(this.get('model.query.repository-id'))) {
          return false;
        } else {
          return true;
        }
      case 'provider_admin':
        return this.get('currentUser.provider_id') === this.get('model.repository.provider.id') || this.get('currentUser.provider_id') === this.get('repository.provider.id');
      default:
        return false;
    }
  }),
  canMove: computed('currentUser.role_id', 'currentUser.provider_id', 'repository.id', 'repository.provider.id', function () {
    switch (this.get('currentUser.role_id')) {
      case 'staff_admin':
        if (w("crossref.citations medra.citations kisti.citations jalc.citations op.citations").includes(this.get('repository.id'))) {
          return false;
        } else {
          return true;
        }
      case 'provider_admin':
        return true; //this.get('currentUser.provider_id') === this.get('repository.provider.id');
      default:
        return false;
    }
  }),
  canUpdate: computed('currentUser.role_id', 'model.id', function () {
    if (w("crossref.citations medra.citations kisti.citations jalc.citations op.citations").includes(this.get('model.id'))) {
      return false;
    } else {
      switch (this.get('currentUser.role_id')) {
      case 'staff_admin':
      case 'provider_admin':
        return true;
      case 'client_admin':
        return this.get('currentUser.repository_id') === this.get('model.id');
      default:
        return false;
      }
    }
  }),
  canUpload: computed('currentUser.role_id', 'currentUser.repository_id', 'model.query.repository-id', function () {
    if (w("crossref.citations medra.citations kisti.citations jalc.citations op.citations").includes(this.get('model.query.repository-id'))) {
      return false;
    } else {
      switch (this.get('currentUser.role_id')) {
      case 'staff_admin':
        return true;
      case 'client_admin':
        return this.get('currentUser.repository_id') === this.get('model.query.repository-id');
      default:
        return false;
      }
    }
  }),
  canCreate: computed('currentUser.role_id', 'currentUser.repository_id', 'model.query.repository-id', function () {
    if (w("crossref.citations medra.citations kisti.citations jalc.citations op.citations").includes(this.get('model.query.repository-id'))) {
      return false;
    } else {
      switch (this.get('currentUser.role_id')) {
      case 'staff_admin':
        return true;
      case 'client_admin':
        return this.get('currentUser.repository_id') === this.get('model.query.repository-id');
      default:
        return false;
      }
    }
  }),
  canDelete: computed('currentUser.role_id', 'model.repository.id', function () {
    switch (this.get('currentUser.role_id')) {
      case 'staff_admin':
        return true;
      case 'client_admin':
        return this.get('currentUser.repository_id') === this.get('model.repository.id');
      default:
        return false;
    }
  }),
  canModify: computed('currentUser.role_id', 'currentUser.repository_id', 'model.repository.id', function () {
    if (w("crossref.citations medra.citations kisti.citations jalc.citations op.citations").includes(this.get('model.repository.id'))) {
      return false;
    } else {
      switch (this.get('currentUser.role_id')) {
        case 'staff_admin':
          return true;
        case 'client_admin':
          return this.get('currentUser.repository_id') === this.get('model.repository.id');
        default:
          return false;
      }
    }
  }),
  canEdit: computed('currentUser.role_id', 'currentUser.repository_id', 'model.repository.id', function () {
    if (w("crossref.citations medra.citations kisti.citations jalc.citations op.citations").includes(this.get('model.repository.id'))) {
      return false;
    } else {
      switch (this.get('currentUser.role_id')) {
        case 'staff_admin':
          return true;
        case 'client_admin':
          return this.get('currentUser.repository_id') === this.get('model.repository.id');
        default:
          return false;
      }
  }
  }),
  canForm: computed('currentUser.role_id', 'currentUser.repository_id', 'model.repository.id', function () {
    switch (this.get('currentUser.role_id')) {
      case 'staff_admin':
        return true;
      case 'client_admin':
        return this.get('currentUser.repository_id') === 'demo.datacite' && this.get('currentUser.repository_id') === this.get('model.repository.id');
      default:
        return false;
    }
  }),
  canDetail: computed('currentUser.role_id', 'currentUser.provider_id', 'currentUser.repository_id', 'model.repository.id', 'model.repository.provider.id', 'model.state', function () {
    switch (this.get('currentUser.role_id')) {
      case 'staff_admin':
        return true;
      case 'provider_admin':
        return this.get('currentUser.provider_id') === this.get('model.repository.provider.id');
      case 'client_admin':
        return this.get('currentUser.repository_id') === this.get('model.repository.id');
      case 'user':
        return this.get('model.state') === 'findable';
      default:
        return false;
    }
  }),
  canRead: computed('currentUser.role_id', 'currentUser.provider_id', 'currentUser.repository_id', 'model.repository.id', 'model.provider.id', 'model.state', function () {
    switch (this.get('currentUser.role_id')) {
      case 'staff_admin':
        return true;
      case 'provider_admin':
        return this.get('currentUser.provider_id') === this.get('model.provider.id');
      case 'client_admin':
        return this.get('currentUser.repository_id') === this.get('model.repository.id');
      case 'user':
        return this.get('model.state') === 'findable';
      default:
        return this.get('model.state') === 'findable';
    }
  })
});
<commit_msg>fix permissions for doi creation<commit_after>import { computed } from '@ember/object';
import { inject as service } from '@ember/service';
import { Ability } from 'ember-can';
import { w } from '@ember/string';

export default Ability.extend({
  currentUser: service(),

  canViewHealth: computed('currentUser.role_id', function () {
    switch (this.get('currentUser.role_id')) {
      case 'staff_admin':
      case 'provider_admin':
        return true;
      default:
        return false;
    }
  }),
  canViewState: computed('currentUser.role_id', function () {
    switch (this.get('currentUser.role_id')) {
      case 'staff_admin':
      case 'provider_admin':
      case 'client_admin':
        return true;
      default:
        return false;
    }
  }),
  canSource: computed('currentUser.role_id', function () {
    switch (this.get('currentUser.role_id')) {
      case 'staff_admin':
        return true;
      default:
        return false;
    }
  }),
  canTransfer: computed('currentUser.role_id', 'currentUser.provider_id', 'model.repository.provider.id', 'repository.provider.id', 'model.repository.id', 'model.query.client-id', function () {
    switch (this.get('currentUser.role_id')) {
      case 'staff_admin':
        if (w("crossref.citations medra.citations kisti.citations jalc.citations op.citations").includes(this.get('model.repository.id')) || w("crossref.citations medra.citations kisti.citations jalc.citations op.citations").includes(this.get('model.query.client-id'))) {
          return false;
        } else {
          return true;
        }
      case 'provider_admin':
        return this.get('currentUser.provider_id') === this.get('model.repository.provider.id') || this.get('currentUser.provider_id') === this.get('repository.provider.id');
      default:
        return false;
    }
  }),
  canMove: computed('currentUser.role_id', 'currentUser.provider_id', 'repository.id', 'repository.provider.id', function () {
    switch (this.get('currentUser.role_id')) {
      case 'staff_admin':
        if (w("crossref.citations medra.citations kisti.citations jalc.citations op.citations").includes(this.get('repository.id'))) {
          return false;
        } else {
          return true;
        }
      case 'provider_admin':
        return true; //this.get('currentUser.provider_id') === this.get('repository.provider.id');
      default:
        return false;
    }
  }),
  canUpdate: computed('currentUser.role_id', 'currentUser.client_id', 'model.id', function () {
    if (w("crossref.citations medra.citations kisti.citations jalc.citations op.citations").includes(this.get('model.id'))) {
      return false;
    } else {
      switch (this.get('currentUser.role_id')) {
      case 'staff_admin':
      case 'provider_admin':
        return true;
      case 'client_admin':
        return this.get('currentUser.client_id') === this.get('model.repository.id');
      default:
        return false;
      }
    }
  }),
  canUpload: computed('currentUser.role_id', 'currentUser.client_id', 'model.query.client-id', function () {
    if (w("crossref.citations medra.citations kisti.citations jalc.citations op.citations").includes(this.get('model.query.client-id'))) {
      return false;
    } else {
      switch (this.get('currentUser.role_id')) {
      case 'staff_admin':
        return true;
      case 'client_admin':
        return this.get('currentUser.client_id') === this.get('model.query.client-id');
      default:
        return false;
      }
    }
  }),
  canCreate: computed('currentUser.role_id', 'currentUser.client_id', 'model.query.client-id', function () {
    if (w("crossref.citations medra.citations kisti.citations jalc.citations op.citations").includes(this.get('model.query.client-id'))) {
      return false;
    } else {
      switch (this.get('currentUser.role_id')) {
      case 'staff_admin':
        return true;
      case 'client_admin':
        return this.get('currentUser.client_id') === this.get('model.query.client-id');
      default:
        return false;
      }
    }
  }),
  canDelete: computed('currentUser.role_id', 'currentUser.client_id', 'model.repository.id', function () {
    switch (this.get('currentUser.role_id')) {
      case 'staff_admin':
        return true;
      case 'client_admin':
        return this.get('currentUser.client_id') === this.get('model.repository.id');
      default:
        return false;
    }
  }),
  canModify: computed('currentUser.role_id', 'currentUser.client_id', 'model.repository.id', function () {
    if (w("crossref.citations medra.citations kisti.citations jalc.citations op.citations").includes(this.get('model.repository.id'))) {
      return false;
    } else {
      switch (this.get('currentUser.role_id')) {
        case 'staff_admin':
          return true;
        case 'client_admin':
          return this.get('currentUser.client_id') === this.get('model.repository.id');
        default:
          return false;
      }
    }
  }),
  canEdit: computed('currentUser.role_id', 'currentUser.client_id', 'model.repository.id', function () {
    if (w("crossref.citations medra.citations kisti.citations jalc.citations op.citations").includes(this.get('model.repository.id'))) {
      return false;
    } else {
      switch (this.get('currentUser.role_id')) {
        case 'staff_admin':
          return true;
        case 'client_admin':
          return this.get('currentUser.client_id') === this.get('model.repository.id');
        default:
          return false;
      }
  }
  }),
  canForm: computed('currentUser.role_id', 'currentUser.client_id', 'model.repository.id', function () {
    switch (this.get('currentUser.role_id')) {
      case 'staff_admin':
        return true;
      case 'client_admin':
        return this.get('currentUser.client_id') === 'demo.datacite' && this.get('currentUser.client_id') === this.get('model.repository.id');
      default:
        return false;
    }
  }),
  canDetail: computed('currentUser.role_id', 'currentUser.provider_id', 'currentUser.client_id', 'model.repository.id', 'model.repository.provider.id', 'model.state', function () {
    switch (this.get('currentUser.role_id')) {
      case 'staff_admin':
        return true;
      case 'provider_admin':
        return this.get('currentUser.provider_id') === this.get('model.repository.provider.id');
      case 'client_admin':
        return this.get('currentUser.client_id') === this.get('model.repository.id');
      case 'user':
        return this.get('model.state') === 'findable';
      default:
        return false;
    }
  }),
  canRead: computed('currentUser.role_id', 'currentUser.provider_id', 'currentUser.client_id', 'model.repository.id', 'model.provider.id', 'model.state', function () {
    switch (this.get('currentUser.role_id')) {
      case 'staff_admin':
        return true;
      case 'provider_admin':
        return this.get('currentUser.provider_id') === this.get('model.provider.id');
      case 'client_admin':
        return this.get('currentUser.client_id') === this.get('model.repository.id');
      case 'user':
        return this.get('model.state') === 'findable';
      default:
        return this.get('model.state') === 'findable';
    }
  })
});
<|endoftext|>