<commit_before>var fs = require('fs');
var path = require('path');
var Future = require('fibers/future');
var _ = require('underscore');
var auth = require('./auth.js');
var config = require('./config.js');
var httpHelpers = require('./http-helpers.js');
var release = require('./release.js');
var files = require('./files.js');
var ServiceConnection = require('./service-connection.js');

// Use uniload to load the packages that we need to open a connection to the
// current package server and use minimongo in memory. We need the following
// packages.
//
// meteor: base package and prerequsite for all others.
// livedata: DDP client interface to make a connection to the package server.
//
// Like all tools, this uses the current release to find the right versions.
// XXX: Does it really?
var getLoadedPackages = _.once(function () {
  var uniload = require('./uniload.js');
  return uniload.load({
    packages: [ 'meteor', 'livedata', 'mongo-livedata'],
    release: release.current.name
  });
});

// Opens a DDP connection to a package server. Loads the packages needed for a
// DDP connection, then calls DDP connect to the package server URL in config,
// using a current user-agent header composed by http-helpers.js.
var openPackageServerConnection = function () {
  return new ServiceConnection(
    getLoadedPackages(),
    config.getPackageServerUrl(),
    { 'User-Agent': httpHelpers.getUserAgent() }
  );
};

// Load the package data that was saved in the local data.json collection from
// the last time we did a sync to the server. This return object consists of
//
//  - collections: an object keyed by the name of server collections, with the
//    records as an array of javascript objects.
//  - syncToken: a syncToken object representing the last time that we talked to
//    the server, to pass into the getRemotePackageData to get the latest
//    updates.
// If there is no data.json file, or the file cannot be parsed, return null for
// the collections and a default syncToken to ask the server for all the data
// from the beginning of time.
exports.loadCachedServerData = function () {
  var noDataToken =  {
    // XXX have a better sync token for "all"
    syncToken: {time: 'Sun, 01 Jan 2012 00:00:00 GMT'},
    collections: null
  };;

  try {
    var data = fs.readFileSync(config.getPackageStorage(), 'utf8');
  } catch (e) {
    if (e.code == 'ENOENT') {
      console.log("No cached server data found on disk.");
      return noDataToken;
    }
    // XXX we should probably return an error to the caller here to
    // figure out how to handle it
    console.log(e.message);
    process.exit(1);
  }
  var ret = noDataToken;
  try {
    ret = JSON.parse(data);
  } catch (err) {
    // XXX error handling
    console.log("Could not parse JSON in data.json.");
  }
  return ret;
};

// Opens a connection to the server, requests and returns new package data that
// we haven't cached on disk. We assume that data is cached chronologically, so
// essentially, we are asking for a diff from the last time that we did this.
// Takes in:
// - syncToken: a syncToken object to be sent to the server that
//   represents the last time that we talked to the server.
//
// Returns an object, containing the following fields:
//  - syncToken: a new syncToken object, that we can pass to the server in the future.
//  - collections: an object keyed by the name of server collections, with the
//    records as an array of javascript objects.
//
// Throws a ServiceConnection.ConnectionTimeoutError if the method call
// times out.
var loadRemotePackageData = function (syncToken) {
  var conn = openPackageServerConnection();
  try {
    var collectionData = conn.call('syncNewPackageData', syncToken);
  } finally {
    conn.close();
  }
  return collectionData;
};

// Take in an ordered list of javascript objects representing collections of
// package data. In each object, the server-side names of collections are keys
// and the values are the mongo records for that collection stored as an
// array. Goes through the the list in order and merges it into the single
// object, with collection names as keys and the arrays of records as
// corresponding values. The inputs list is ordered and records in the later
// collections will override the records in the earlier collections.
var mergeCollections = function (sources) {
  var collections = {}; // map from collection to _id to object

  _.each(sources, function (source) {
    _.each(source, function (records, collectionName) {
      if (! _.has(collections, collectionName))
        collections[collectionName] = {};

      _.each(records, function (record) {
        collections[collectionName][record._id] = record;
      });
    });
  });

  var ret = {};
  _.each(collections, function (records, collectionName) {
    ret[collectionName] = _.values(records);
  });

  return ret;
};

// Writes the cached package data to the on-disk cache. Takes in the following
// arguments:
// - syncToken : the token representing our conversation with the server, that
//   we can later use to get a diff of this cache and the new server-side data.
// - collectionData : a javascript object representing the data we have about
//   packages on the server, with collection names as keys and arrays of those
//   collection records as values.
//
// Returns nothing, but
// XXXX: Does what on errors?
var writePackageDataToDisk = function (syncToken, collectionData) {
  var finalWrite = {};
  finalWrite.syncToken = syncToken;
  finalWrite.formatVersion = "1.0";
  finalWrite.collections = {};
  _.forEach(collectionData, function(coll, name) {
    finalWrite.collections[name] = coll;
  });
  var filename = config.getPackageStorage();
  // XXX think about permissions?
  files.mkdir_p(path.dirname(filename));
  files.writeFileAtomically(filename, JSON.stringify(finalWrite, null, 2));
};

// Contacts the package server to get the latest diff and writes changes to
// disk.
//
// Takes in cachedServerData, which is the processed contents of data.json. Uses
// those to talk to the server and get the latest updates. Applies the diff from
// the server to the in-memory version of the on-disk data, then writes the new
// file to disk as the new data.json.
//
// Returns null if contacting the server times out.
exports.updateServerPackageData = function (cachedServerData) {
  var sources = [];
  if (cachedServerData.collections) {
    sources.push(cachedServerData.collections);
  }
  var syncToken = cachedServerData.syncToken;
  var remoteData;
  try {
    remoteData = loadRemotePackageData(syncToken);
  } catch (err) {
    if (err instanceof ServiceConnection.ConnectionTimeoutError) {
      return null;
    } else {
      throw err;
    }
  }
  sources.push(remoteData.collections);

  var allPackageData = mergeCollections(sources);
  writePackageDataToDisk(remoteData.syncToken, allPackageData);
  return allPackageData;
};

// Returns a logged-in DDP connection to the package server, or null if
// we cannot log in. If an error unrelated to login occurs
// (e.g. connection to package server times out), then it will be
// thrown.
exports.loggedInPackagesConnection = function () {
  // Make sure that we are logged in with Meteor Accounts so that we can
  // do an OAuth flow.
  if (! auth.isLoggedIn()) {
    auth.doUsernamePasswordLogin({ retry: true });
  }

  var conn = openPackageServerConnection();

  var setUpOnReconnect = function () {
    conn.onReconnect = function () {
      conn.apply('login', [{
        resume: auth.getSessionToken(config.getPackageServerDomain())
      }], { wait: true }, function () { });
    };
  };

  // Subscribe to the package server's service configurations so that we
  // can get the OAuth client ID to kick off the OAuth flow.
  var serviceConfigurations = new (getLoadedPackages().meteor.Meteor.Collection)(
    'meteor_accounts_loginServiceConfiguration',
    { connection: conn.connection }
  );
  var serviceConfigurationsSub = conn.
        subscribeAndWait('meteor.loginServiceConfiguration');

  var accountsConfiguration = serviceConfigurations.findOne({
    service: 'meteor-developer'
  });

  var cleanUp = function () {
    serviceConfigurationsSub && serviceConfigurationsSub.stop();
    conn && conn.close();
  };

  if (! accountsConfiguration || ! accountsConfiguration.clientId) {
    console.log(serviceConfigurations.find().fetch());
    cleanUp();
    return null;
  }

  var clientId = accountsConfiguration.clientId;
  var loginResult;

  // Try to log in with an existing login token, if we have one.
  var existingToken = auth.getSessionToken(config.getPackageServerDomain());
  if (existingToken) {
    try {
      loginResult = conn.apply('login', [{
        resume: existingToken
      }], { wait: true });
    } catch (err) {
      // If we get a Meteor.Error, then we swallow it and go on to
      // attempt an OAuth flow and get a new token. If it's not a
      // Meteor.Error, then we leave it to the caller to handle.
      if (! err instanceof getLoadedPackages().meteor.Meteor.Error) {
        cleanUp();
        throw err;
      }
    }

    if (loginResult && loginResult.token && loginResult.id) {
      // Success!
      setUpOnReconnect();
      return conn;
    }
  }

  // Either we didn't have an existing token, or it didn't work. Do an
  // OAuth flow to log in.
  var redirectUri = config.getPackageServerUrl() +
        '/_oauth/meteor-developer?close';
  loginResult = auth.oauthFlow(conn, {
    clientId: clientId,
    redirectUri: redirectUri,
    domain: config.getPackageServerDomain(),
    sessionType: 'package-server'
  });

  if (loginResult && ! loginResult.error) {
    setUpOnReconnect();
    return conn;
  } else {
    if (loginResult.error === "login-failed" ||
        loginResult.error === "access-denied") {
      process.stderr.write('Error logging in to package server: ' +
                           loginResult.error + '\n');
      cleanUp();
      return null;
    } else {
      cleanUp();
      throw new Error(loginResult.error);
    }
  }
};

var hashTarball = function (tarball) {
  var crypto = require('crypto');
  var hash = crypto.createHash('sha256');
  hash.setEncoding('base64');
  var rs = fs.createReadStream(tarball);
  var fut = new Future();
  rs.on('end', function () {
    fut.return(hash.digest('base64'));
  });
  rs.pipe(hash, { end: false });
  var tarballHash = fut.wait();
  rs.close();
  return tarballHash;
};

// XXX this is missing a few things:
//    - locking down build-time dependencies: tools version, versions
//      of all (not-built-from-source) plugins used
// in general, we need to include all the stuff that goes into the watchSet
// We include npm-shrinkwrap which does not go in the watchSet but
// probably should.
//
// In retrospect a better approach here might be to actually make "save source
// somewhere else" or perhaps "add source to tarball" be part of the package
// build itself...
exports.bundleSource = function (unipackage, includeSources, packageDir) {
  var name = unipackage.name;

  var tempDir = files.mkdtemp('build-source-package-');
  var packageTarName = name + '-' + unipackage.version + '-source';
  var dirToTar = path.join(tempDir, 'source', packageTarName);
  var sourcePackageDir = path.join(
    dirToTar,
    name
  );
  if (! files.mkdir_p(sourcePackageDir)) {
    process.stderr.write('Failed to create temporary source directory: ' +
                         sourcePackageDir);
    return null;
  }

  includeSources.push('package.js');
  if (fs.existsSync('.npm/package/npm-shrinkwrap.json')) {
    includeSources.push('.npm/package/npm-shrinkwrap.json');
  }
  _.each(unipackage.plugins, function (plugin, pluginName) {
    var pluginShrinkwrap = path.join('.npm/plugin/', pluginName,
                                     'npm-shrinkwrap.json');
    if (fs.existsSync(pluginShrinkwrap)) {
      includeSources.push(pluginShrinkwrap);
    }
  });

  // We copy source files into a temp directory and then tar up the temp
  // directory. It would be great if we could avoid the copy, but as far
  // as we can tell, this is the only way to get a tarball with the
  // directory structure that we want (<package name>-<version-source/
  // at the top level).
  files.cp_r(packageDir, sourcePackageDir, {
    include: includeSources
  });

  // We put this inside the temp dir because mkdtemp makes sure that the
  // temp dir gets cleaned up on process exit, so we don't have to worry
  // about cleaning up our tarball (or our copied source files)
  // ourselves.
  var sourceTarball = path.join(tempDir, packageTarName + '.tgz');
  files.createTarball(dirToTar, sourceTarball);

  var tarballHash = hashTarball(sourceTarball);

  return {
    sourceTarball: sourceTarball,
    tarballHash: tarballHash
  };
};

var uploadTarball = function (putUrl, tarball) {
  var size = fs.statSync(tarball).size;
  var rs = fs.createReadStream(tarball);
  httpHelpers.request({
    method: 'PUT',
    url: putUrl,
    headers: {
      'content-length': size,
      'content-type': 'application/octet-stream',
      'x-amz-acl': 'public-read'
    },
    bodyStream: rs
  });
  rs.close();
};

exports.uploadTarball = uploadTarball;

var bundleBuild = function (unipackage, packageDir) {
  var tempDir = files.mkdtemp('build-package-');
  var packageTarName = unipackage.name + '-' + unipackage.version + '-' +
        unipackage.architectures().join('+');
  var tarInputDir = path.join(tempDir, packageTarName);

  files.cp_r(path.join(packageDir, '.build'), tarInputDir);

  // Don't upload buildinfo.json. It's only of interest locally (for
  // example, it contains a watchset with local paths).
  var buildInfoPath = path.join(tarInputDir, 'buildinfo.json');
  if (fs.existsSync(buildInfoPath))
    fs.unlinkSync(buildInfoPath);

  var buildTarball = path.join(tempDir, packageTarName + '.tgz');
  files.createTarball(tarInputDir, buildTarball);

  var tarballHash = hashTarball(buildTarball);

  return {
    buildTarball: buildTarball,
    tarballHash: tarballHash
  };
};

exports.bundleBuild = bundleBuild;

exports.createAndPublishBuiltPackage = function (conn, unipackage, packageDir) {
  process.stdout.write('Creating package build...\n');
  var uploadInfo = conn.call('createPackageBuild', {
    packageName: unipackage.name,
    version: unipackage.version,
    architecture: unipackage.architectures().join('+')
  });

  var bundleResult = bundleBuild(unipackage, packageDir);

  process.stdout.write('Uploading build...\n');
  uploadTarball(uploadInfo.uploadUrl,
                bundleResult.buildTarball);

  process.stdout.write('Publishing package build...\n');
  conn.call('publishPackageBuild',
            uploadInfo.uploadToken, bundleResult.tarballHash);

  conn.close();
  process.stdout.write('Published ' + unipackage.name +
                       ', version ' + unipackage.version);

  process.stdout.write('\nDone!\n');
};

exports.handlePackageServerConnectionError = function (error) {
  var Package = getLoadedPackages();
  if (error instanceof Package.meteor.Meteor.Error) {
    process.stderr.write("Error connecting to package server");
    if (error.message) {
      process.stderr.write(": " + error.message);
    }
    process.stderr.write("\n");
  } else if (error instanceof ServiceConnection.ConnectionTimeoutError) {
    process.stderr.write("Connection to package server timed out.\n");
  } else {
    throw error;
  }
};
<commit_msg>publish with the right build directory<commit_after>var fs = require('fs');
var path = require('path');
var Future = require('fibers/future');
var _ = require('underscore');
var auth = require('./auth.js');
var config = require('./config.js');
var httpHelpers = require('./http-helpers.js');
var release = require('./release.js');
var files = require('./files.js');
var ServiceConnection = require('./service-connection.js');

// Use uniload to load the packages that we need to open a connection to the
// current package server and use minimongo in memory. We need the following
// packages.
//
// meteor: base package and prerequsite for all others.
// livedata: DDP client interface to make a connection to the package server.
//
// Like all tools, this uses the current release to find the right versions.
// XXX: Does it really?
var getLoadedPackages = _.once(function () {
  var uniload = require('./uniload.js');
  return uniload.load({
    packages: [ 'meteor', 'livedata', 'mongo-livedata'],
    release: release.current.name
  });
});

// Opens a DDP connection to a package server. Loads the packages needed for a
// DDP connection, then calls DDP connect to the package server URL in config,
// using a current user-agent header composed by http-helpers.js.
var openPackageServerConnection = function () {
  return new ServiceConnection(
    getLoadedPackages(),
    config.getPackageServerUrl(),
    { 'User-Agent': httpHelpers.getUserAgent() }
  );
};

// Load the package data that was saved in the local data.json collection from
// the last time we did a sync to the server. This return object consists of
//
//  - collections: an object keyed by the name of server collections, with the
//    records as an array of javascript objects.
//  - syncToken: a syncToken object representing the last time that we talked to
//    the server, to pass into the getRemotePackageData to get the latest
//    updates.
// If there is no data.json file, or the file cannot be parsed, return null for
// the collections and a default syncToken to ask the server for all the data
// from the beginning of time.
exports.loadCachedServerData = function () {
  var noDataToken =  {
    // XXX have a better sync token for "all"
    syncToken: {time: 'Sun, 01 Jan 2012 00:00:00 GMT'},
    collections: null
  };;

  try {
    var data = fs.readFileSync(config.getPackageStorage(), 'utf8');
  } catch (e) {
    if (e.code == 'ENOENT') {
      console.log("No cached server data found on disk.");
      return noDataToken;
    }
    // XXX we should probably return an error to the caller here to
    // figure out how to handle it
    console.log(e.message);
    process.exit(1);
  }
  var ret = noDataToken;
  try {
    ret = JSON.parse(data);
  } catch (err) {
    // XXX error handling
    console.log("Could not parse JSON in data.json.");
  }
  return ret;
};

// Opens a connection to the server, requests and returns new package data that
// we haven't cached on disk. We assume that data is cached chronologically, so
// essentially, we are asking for a diff from the last time that we did this.
// Takes in:
// - syncToken: a syncToken object to be sent to the server that
//   represents the last time that we talked to the server.
//
// Returns an object, containing the following fields:
//  - syncToken: a new syncToken object, that we can pass to the server in the future.
//  - collections: an object keyed by the name of server collections, with the
//    records as an array of javascript objects.
//
// Throws a ServiceConnection.ConnectionTimeoutError if the method call
// times out.
var loadRemotePackageData = function (syncToken) {
  var conn = openPackageServerConnection();
  try {
    var collectionData = conn.call('syncNewPackageData', syncToken);
  } finally {
    conn.close();
  }
  return collectionData;
};

// Take in an ordered list of javascript objects representing collections of
// package data. In each object, the server-side names of collections are keys
// and the values are the mongo records for that collection stored as an
// array. Goes through the the list in order and merges it into the single
// object, with collection names as keys and the arrays of records as
// corresponding values. The inputs list is ordered and records in the later
// collections will override the records in the earlier collections.
var mergeCollections = function (sources) {
  var collections = {}; // map from collection to _id to object

  _.each(sources, function (source) {
    _.each(source, function (records, collectionName) {
      if (! _.has(collections, collectionName))
        collections[collectionName] = {};

      _.each(records, function (record) {
        collections[collectionName][record._id] = record;
      });
    });
  });

  var ret = {};
  _.each(collections, function (records, collectionName) {
    ret[collectionName] = _.values(records);
  });

  return ret;
};

// Writes the cached package data to the on-disk cache. Takes in the following
// arguments:
// - syncToken : the token representing our conversation with the server, that
//   we can later use to get a diff of this cache and the new server-side data.
// - collectionData : a javascript object representing the data we have about
//   packages on the server, with collection names as keys and arrays of those
//   collection records as values.
//
// Returns nothing, but
// XXXX: Does what on errors?
var writePackageDataToDisk = function (syncToken, collectionData) {
  var finalWrite = {};
  finalWrite.syncToken = syncToken;
  finalWrite.formatVersion = "1.0";
  finalWrite.collections = {};
  _.forEach(collectionData, function(coll, name) {
    finalWrite.collections[name] = coll;
  });
  var filename = config.getPackageStorage();
  // XXX think about permissions?
  files.mkdir_p(path.dirname(filename));
  files.writeFileAtomically(filename, JSON.stringify(finalWrite, null, 2));
};

// Contacts the package server to get the latest diff and writes changes to
// disk.
//
// Takes in cachedServerData, which is the processed contents of data.json. Uses
// those to talk to the server and get the latest updates. Applies the diff from
// the server to the in-memory version of the on-disk data, then writes the new
// file to disk as the new data.json.
//
// Returns null if contacting the server times out.
exports.updateServerPackageData = function (cachedServerData) {
  var sources = [];
  if (cachedServerData.collections) {
    sources.push(cachedServerData.collections);
  }
  var syncToken = cachedServerData.syncToken;
  var remoteData;
  try {
    remoteData = loadRemotePackageData(syncToken);
  } catch (err) {
    if (err instanceof ServiceConnection.ConnectionTimeoutError) {
      return null;
    } else {
      throw err;
    }
  }
  sources.push(remoteData.collections);

  var allPackageData = mergeCollections(sources);
  writePackageDataToDisk(remoteData.syncToken, allPackageData);
  return allPackageData;
};

// Returns a logged-in DDP connection to the package server, or null if
// we cannot log in. If an error unrelated to login occurs
// (e.g. connection to package server times out), then it will be
// thrown.
exports.loggedInPackagesConnection = function () {
  // Make sure that we are logged in with Meteor Accounts so that we can
  // do an OAuth flow.
  if (! auth.isLoggedIn()) {
    auth.doUsernamePasswordLogin({ retry: true });
  }

  var conn = openPackageServerConnection();

  var setUpOnReconnect = function () {
    conn.onReconnect = function () {
      conn.apply('login', [{
        resume: auth.getSessionToken(config.getPackageServerDomain())
      }], { wait: true }, function () { });
    };
  };

  // Subscribe to the package server's service configurations so that we
  // can get the OAuth client ID to kick off the OAuth flow.
  var serviceConfigurations = new (getLoadedPackages().meteor.Meteor.Collection)(
    'meteor_accounts_loginServiceConfiguration',
    { connection: conn.connection }
  );
  var serviceConfigurationsSub = conn.
        subscribeAndWait('meteor.loginServiceConfiguration');

  var accountsConfiguration = serviceConfigurations.findOne({
    service: 'meteor-developer'
  });

  var cleanUp = function () {
    serviceConfigurationsSub && serviceConfigurationsSub.stop();
    conn && conn.close();
  };

  if (! accountsConfiguration || ! accountsConfiguration.clientId) {
    console.log(serviceConfigurations.find().fetch());
    cleanUp();
    return null;
  }

  var clientId = accountsConfiguration.clientId;
  var loginResult;

  // Try to log in with an existing login token, if we have one.
  var existingToken = auth.getSessionToken(config.getPackageServerDomain());
  if (existingToken) {
    try {
      loginResult = conn.apply('login', [{
        resume: existingToken
      }], { wait: true });
    } catch (err) {
      // If we get a Meteor.Error, then we swallow it and go on to
      // attempt an OAuth flow and get a new token. If it's not a
      // Meteor.Error, then we leave it to the caller to handle.
      if (! err instanceof getLoadedPackages().meteor.Meteor.Error) {
        cleanUp();
        throw err;
      }
    }

    if (loginResult && loginResult.token && loginResult.id) {
      // Success!
      setUpOnReconnect();
      return conn;
    }
  }

  // Either we didn't have an existing token, or it didn't work. Do an
  // OAuth flow to log in.
  var redirectUri = config.getPackageServerUrl() +
        '/_oauth/meteor-developer?close';
  loginResult = auth.oauthFlow(conn, {
    clientId: clientId,
    redirectUri: redirectUri,
    domain: config.getPackageServerDomain(),
    sessionType: 'package-server'
  });

  if (loginResult && ! loginResult.error) {
    setUpOnReconnect();
    return conn;
  } else {
    if (loginResult.error === "login-failed" ||
        loginResult.error === "access-denied") {
      process.stderr.write('Error logging in to package server: ' +
                           loginResult.error + '\n');
      cleanUp();
      return null;
    } else {
      cleanUp();
      throw new Error(loginResult.error);
    }
  }
};

var hashTarball = function (tarball) {
  var crypto = require('crypto');
  var hash = crypto.createHash('sha256');
  hash.setEncoding('base64');
  var rs = fs.createReadStream(tarball);
  var fut = new Future();
  rs.on('end', function () {
    fut.return(hash.digest('base64'));
  });
  rs.pipe(hash, { end: false });
  var tarballHash = fut.wait();
  rs.close();
  return tarballHash;
};

// XXX this is missing a few things:
//    - locking down build-time dependencies: tools version, versions
//      of all (not-built-from-source) plugins used
// in general, we need to include all the stuff that goes into the watchSet
// We include npm-shrinkwrap which does not go in the watchSet but
// probably should.
//
// In retrospect a better approach here might be to actually make "save source
// somewhere else" or perhaps "add source to tarball" be part of the package
// build itself...
exports.bundleSource = function (unipackage, includeSources, packageDir) {
  var name = unipackage.name;

  var tempDir = files.mkdtemp('build-source-package-');
  var packageTarName = name + '-' + unipackage.version + '-source';
  var dirToTar = path.join(tempDir, 'source', packageTarName);
  var sourcePackageDir = path.join(
    dirToTar,
    name
  );
  if (! files.mkdir_p(sourcePackageDir)) {
    process.stderr.write('Failed to create temporary source directory: ' +
                         sourcePackageDir);
    return null;
  }

  includeSources.push('package.js');
  if (fs.existsSync('.npm/package/npm-shrinkwrap.json')) {
    includeSources.push('.npm/package/npm-shrinkwrap.json');
  }
  _.each(unipackage.plugins, function (plugin, pluginName) {
    var pluginShrinkwrap = path.join('.npm/plugin/', pluginName,
                                     'npm-shrinkwrap.json');
    if (fs.existsSync(pluginShrinkwrap)) {
      includeSources.push(pluginShrinkwrap);
    }
  });

  // We copy source files into a temp directory and then tar up the temp
  // directory. It would be great if we could avoid the copy, but as far
  // as we can tell, this is the only way to get a tarball with the
  // directory structure that we want (<package name>-<version-source/
  // at the top level).
  files.cp_r(packageDir, sourcePackageDir, {
    include: includeSources
  });

  // We put this inside the temp dir because mkdtemp makes sure that the
  // temp dir gets cleaned up on process exit, so we don't have to worry
  // about cleaning up our tarball (or our copied source files)
  // ourselves.
  var sourceTarball = path.join(tempDir, packageTarName + '.tgz');
  files.createTarball(dirToTar, sourceTarball);

  var tarballHash = hashTarball(sourceTarball);

  return {
    sourceTarball: sourceTarball,
    tarballHash: tarballHash
  };
};

var uploadTarball = function (putUrl, tarball) {
  var size = fs.statSync(tarball).size;
  var rs = fs.createReadStream(tarball);
  httpHelpers.request({
    method: 'PUT',
    url: putUrl,
    headers: {
      'content-length': size,
      'content-type': 'application/octet-stream',
      'x-amz-acl': 'public-read'
    },
    bodyStream: rs
  });
  rs.close();
};

exports.uploadTarball = uploadTarball;

var bundleBuild = function (unipackage, packageDir) {
  var tempDir = files.mkdtemp('build-package-');
  var packageTarName = unipackage.name + '-' + unipackage.version + '-' +
        unipackage.architectures().join('+');
  var tarInputDir = path.join(tempDir, packageTarName);

  files.cp_r(path.join(packageDir, '.build.'+unipackage.name), tarInputDir);

  // Don't upload buildinfo.json. It's only of interest locally (for
  // example, it contains a watchset with local paths).
  var buildInfoPath = path.join(tarInputDir, 'buildinfo.json');
  if (fs.existsSync(buildInfoPath))
    fs.unlinkSync(buildInfoPath);

  var buildTarball = path.join(tempDir, packageTarName + '.tgz');
  files.createTarball(tarInputDir, buildTarball);

  var tarballHash = hashTarball(buildTarball);

  return {
    buildTarball: buildTarball,
    tarballHash: tarballHash
  };
};

exports.bundleBuild = bundleBuild;

exports.createAndPublishBuiltPackage = function (conn, unipackage, packageDir) {
  process.stdout.write('Creating package build...\n');
  var uploadInfo = conn.call('createPackageBuild', {
    packageName: unipackage.name,
    version: unipackage.version,
    architecture: unipackage.architectures().join('+')
  });

  var bundleResult = bundleBuild(unipackage, packageDir);

  process.stdout.write('Uploading build...\n');
  uploadTarball(uploadInfo.uploadUrl,
                bundleResult.buildTarball);

  process.stdout.write('Publishing package build...\n');
  conn.call('publishPackageBuild',
            uploadInfo.uploadToken, bundleResult.tarballHash);

  conn.close();
  process.stdout.write('Published ' + unipackage.name +
                       ', version ' + unipackage.version);

  process.stdout.write('\nDone!\n');
};

exports.handlePackageServerConnectionError = function (error) {
  var Package = getLoadedPackages();
  if (error instanceof Package.meteor.Meteor.Error) {
    process.stderr.write("Error connecting to package server");
    if (error.message) {
      process.stderr.write(": " + error.message);
    }
    process.stderr.write("\n");
  } else if (error instanceof ServiceConnection.ConnectionTimeoutError) {
    process.stderr.write("Connection to package server timed out.\n");
  } else {
    throw error;
  }
};
<|endoftext|>


<commit_before>var express = require('express'),
fs = require('fs'),
path = require('path'),
url = require('url'),
request = require('request'),
dns = require('dns'),
jimp = require('jimp'),
tmp = require("tmp"),
common;

var metahub_srv = "bokeh-metahub.service.consul."
var photohub_srv = "bokeh-photohub-3000.service.consul."
var thumbhub_srv = "bokeh-thumbhub.service.consul."

function pickupSRV(name, cb) {
	dns.resolveSrv(name, function (err, results) {
		if (results instanceof Array) {
			// Pickup a random result from the different resolved names
			result = results[Math.floor(Math.random()*results.length)];
			cb(result);
		}
	});
}

function getMIMEtype(hash, cb) {
	pickupSRV(metahub_srv, function(record) {
		var myurl = 'http://' + record.name + ':' + record.port + '/photos?hash=' + hash;
		console.log("Requesting Metahub: "+ myurl);
		request({uri: myurl}).on('response', function(response) {
			var str = '';
			response.on('data', function (chunk) { str += chunk; });
			response.on('end', function () {
				console.log("Got answer from Metahub: " + str);
				var resp = JSON.parse(str);
				if (resp) {
					myPhoto = resp['photos'][0];
					type = myPhoto['type'];
					cb(false, type);
				} else {
					console.log('Bad response from Metahub');
					cb('error', '');
				}
			});
		}).on('error', function (error) {
			console.log('Error while requesting Metahub');
			cb('error', '');
		});
	});
}

module.exports = function(config){
	var app = express(),
	staticFiles = config.staticFiles,
	common = require('./common')(config);

//	app.get(/.+\.(jpg|bmp|jpeg|gif|png|tif)$/i, function(req, res, next){
	app.get(/.+$/i, function(req, res, next){
		var filePath = path.join(staticFiles, req.path),
		fstream;
		var filename = path.parse(req.path).base;

		console.log("Got request for " + config.urlRoot + req.path);

		//filePath = decodeURI(filePath);

		getMIMEtype(filename, function (e, type) {
			if (e) {
				console.log("Unable to get MIME type on MetaHub");
				return common.error(req, res, next, 404, 'File not found', err);
			}
			console.log("Got from MetaHub type: " + type)
			fs.stat(filePath, function(err){
				if (err){
					console.log("Thumb not found: " + req.path);
					tmpFile = tmp.fileSync();
					pickupSRV(photohub_srv, function(record) {
						var myurl = 'http://' + record.name + ':' + record.port + '/photos/hash/' + filename;
						console.log('Uploading photo from PhotoHub: '+myurl);
						jimp.read(myurl, function(err, img) {
							if (err) {
								console.log("Cannot download: " + myurl);
								return common.error(req, res, next, 404, 'File not found', err);
							}
							img.getBuffer(type, function(err, data){
								console.log("Img: " + img.getMIME());
								jimp.read(data).then(function (image) {
									img.resize(256, jimp.AUTO).write(filePath, function(err, img) {
										if (err) {
											console.log("Cannot write final thumb: " + filePath + " err: " + err);
											return common.error(req, res, next, 404, 'File not found', err);
										}
										console.log("Successfully created thumb: " + filePath);
										fstream = fs.createReadStream(filePath);
										return fstream.pipe(res);
									});
								}).catch(function (e) {
									console.log("Cannot red buffer: " + e);
									return common.error(req, res, next, 404, 'File not found', err);
								});
							});
						});
					});
				} else {
					fstream = fs.createReadStream(filePath);
					fstream.on('error', function(err){
						console.log("Cannot read thumb: " + req.path);
						return common.error(req, res, next, 404, 'File not found', err);
					});
					return fstream.pipe(res);
				}
			});
		});
	});

	return app;
}<commit_msg>TH: add log<commit_after>var express = require('express'),
fs = require('fs'),
path = require('path'),
url = require('url'),
request = require('request'),
dns = require('dns'),
jimp = require('jimp'),
tmp = require("tmp"),
common;

var metahub_srv = "bokeh-metahub.service.consul."
var photohub_srv = "bokeh-photohub-3000.service.consul."
var thumbhub_srv = "bokeh-thumbhub.service.consul."

function pickupSRV(name, cb) {
	dns.resolveSrv(name, function (err, results) {
		if (results instanceof Array) {
			// Pickup a random result from the different resolved names
			result = results[Math.floor(Math.random()*results.length)];
			cb(result);
		}
	});
}

function getMIMEtype(hash, cb) {
	pickupSRV(metahub_srv, function(record) {
		var myurl = 'http://' + record.name + ':' + record.port + '/photos?hash=' + hash;
		console.log("Requesting Metahub: "+ myurl);
		request({uri: myurl}).on('response', function(response) {
			var str = '';
			response.on('data', function (chunk) { str += chunk; });
			response.on('end', function () {
				console.log("Got answer from Metahub: " + str);
				var resp = JSON.parse(str);
				if (resp) {
					myPhoto = resp['photos'][0];
					type = myPhoto['type'];
					cb(false, type);
				} else {
					console.log('Bad response from Metahub');
					cb('error', '');
				}
			});
		}).on('error', function (error) {
			console.log('Error while requesting Metahub');
			cb('error', '');
		});
	});
}

module.exports = function(config){
	var app = express(),
	staticFiles = config.staticFiles,
	common = require('./common')(config);

//	app.get(/.+\.(jpg|bmp|jpeg|gif|png|tif)$/i, function(req, res, next){
	app.get(/.+$/i, function(req, res, next){
		var filePath = path.join(staticFiles, req.path),
		fstream;
		var filename = path.parse(req.path).base;

		console.log("Got request for " + config.urlRoot + req.path);

		//filePath = decodeURI(filePath);

		getMIMEtype(filename, function (e, type) {
			if (e) {
				console.log("Unable to get MIME type on MetaHub");
				return common.error(req, res, next, 404, 'File not found', err);
			}
			console.log("Got from MetaHub type: " + type)
			fs.stat(filePath, function(err){
				if (err){
					console.log("Thumb not found: " + req.path);
					tmpFile = tmp.fileSync();
					pickupSRV(photohub_srv, function(record) {
						var myurl = 'http://' + record.name + ':' + record.port + '/photos/hash/' + filename;
						console.log('Uploading photo from PhotoHub: '+myurl);
						jimp.read(myurl, function(err, img) {
							if (err) {
								console.log("Cannot download: " + err);
								return common.error(req, res, next, 404, 'File not found', err);
							}
							img.getBuffer(type, function(err, data){
								console.log("Img: " + img.getMIME());
								jimp.read(data).then(function (image) {
									img.resize(256, jimp.AUTO).write(filePath, function(err, img) {
										if (err) {
											console.log("Cannot write final thumb: " + filePath + " err: " + err);
											return common.error(req, res, next, 404, 'File not found', err);
										}
										console.log("Successfully created thumb: " + filePath);
										fstream = fs.createReadStream(filePath);
										return fstream.pipe(res);
									});
								}).catch(function (e) {
									console.log("Cannot red buffer: " + e);
									return common.error(req, res, next, 404, 'File not found', err);
								});
							});
						});
					});
				} else {
					fstream = fs.createReadStream(filePath);
					fstream.on('error', function(err){
						console.log("Cannot read thumb: " + req.path);
						return common.error(req, res, next, 404, 'File not found', err);
					});
					return fstream.pipe(res);
				}
			});
		});
	});

	return app;
}<|endoftext|>


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

var j4p = new Jolokia({url: "http://jolokia.org/jolokia", fetchInterval: 1000});

var context = cubism.context()
    .serverDelay(0)
    .clientDelay(0)
    .step(1000)
    .size(594);
var jolokia = context.jolokia(j4p);

// =============================================================================
// Various metrics used

var memory = jolokia.metric(
    function (resp1, resp2) {
        return Number(resp1.value) / Number(resp2.value);
    },
    {type:"read", mbean:"java.lang:type=Memory", attribute:"HeapMemoryUsage", path:"used"},
    {type:"read", mbean:"java.lang:type=Memory", attribute:"HeapMemoryUsage", path:"max"}, "Heap-Memory"
);
var gcCount = jolokia.metric(
    {type:"read", mbean:"java.lang:name=PS MarkSweep,type=GarbageCollector", attribute:"CollectionCount"},
    {delta:1000, name:"GC Old"}
);
var gcCount2 = jolokia.metric(
    {type:"read", mbean:"java.lang:name=PS Scavenge,type=GarbageCollector", attribute:"CollectionCount"},
    {delta:1000, name:"GC Young"}
);

var agentRequest = jolokia.metric(
    {type:        "read", mbean:"Jolokia:J2EEApplication=none,J2EEServer=none,WebModule=//localhost/jolokia,j2eeType=Servlet,name=jolokia-agent",
        attribute:"requestCount"}, {name:"Jolokia", delta:10 * 1000});
var jenkinsRequest = jolokia.metric(
    {type:        "read", mbean:"Catalina:J2EEApplication=none,J2EEServer=none,WebModule=//localhost/jenkins,j2eeType=Servlet,name=Stapler",
        attribute:"requestCount"}, {name:"Jenkins", delta:10 * 1000});
var allRequests = jolokia.metric(
    function (resp) {
        var attrs = resp.value;
        var sum = 0;
        for (var key in attrs) {
            sum += attrs[key].requestCount;
        }
        return sum;
    },
    {type:        "read", mbean:"Catalina:j2eeType=Servlet,*",
        attribute:"requestCount"}, {name:"All", delta:10 * 1000});

//j4p.start(1000);

var colorsRed = ["#FDBE85", "#FEEDDE", "#FD8D3C", "#E6550D", "#A63603", "#FDBE85", "#FEEDDE", "#FD8D3C", "#E6550D", "#A63603" ],
    colorsGreen = [ "#E5F5F9", "#99D8C9", "#2CA25F", "#E5F5F9", "#99D8C9", "#2CA25F"],
    colorsBlue = [ "#ECE7F2", "#A6BDDB", "#2B8CBE", "#ECE7F2", "#A6BDDB", "#2B8CBE"];

// Created graphs
$(function () {
    d3.select("#memory").call(function (div) {

        div.append("div")
            .attr("class", "axis")
            .call(context.axis().orient("top"));

        div.selectAll(".horizon")
            .data([memory])
            .enter().append("div")
            .attr("class", "horizon")
            .call(
            context.horizon()
                .colors(colorsRed)
                .format(d3.format(".4p"))
        );
        div.selectAll(".horizon-gc")
            .data([gcCount2, gcCount])
            .enter().append("div")
            .attr("class", "horizon horizon-gc")
            .call(
            context.horizon().colors(colorsRed).height(10)
        );
        div.append("div")
            .attr("class", "rule")
            .call(context.rule());

    });

    d3.select("#request").call(function (div) {

        div.append("div")
            .attr("class", "axis")
            .call(context.axis().orient("top"));

        div.selectAll(".horizon")
            .data([agentRequest, jenkinsRequest, allRequests])
            .enter()
            .append("div")
            .attr("class", "horizon")
            .call(context.horizon()
            .format(d3.format("10d"))
            .colors(function (d, i) {
                return i == 3 ? colorsBlue : colorsGreen
            }));

        div.append("div")
            .attr("class", "rule")
            .call(context.rule());

    });

    // On mousemove, reposition the chart values to match the rule.
    context.on("focus", function (i) {
        d3.selectAll("#memory .value").style("right", i == null ? null : context.size() - i + "px");
        d3.selectAll("#request .value").style("right", i == null ? null : context.size() - i + "px");
    });

});

function gc() {
    j4p.request({type:"exec", mbean:"java.lang:type=Memory", operation:"gc"})
}
<commit_msg>Updated demo to current labs.consol.de setpup .<commit_after>/*
 * Copyright 2009-2013 Roland Huss
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var j4p = new Jolokia({url: "http://jolokia.org/jolokia", fetchInterval: 1000});

var context = cubism.context()
    .serverDelay(0)
    .clientDelay(0)
    .step(1000)
    .size(594);
var jolokia = context.jolokia(j4p);

// =============================================================================
// Various metrics used

var memory = jolokia.metric(
    function (resp1, resp2) {
        return Number(resp1.value) / Number(resp2.value);
    },
    {type:"read", mbean:"java.lang:type=Memory", attribute:"HeapMemoryUsage", path:"used"},
    {type:"read", mbean:"java.lang:type=Memory", attribute:"HeapMemoryUsage", path:"max"}, "Heap-Memory"
);
var gcCount = jolokia.metric(
    {type:"read", mbean:"java.lang:name=PS MarkSweep,type=GarbageCollector", attribute:"CollectionCount"},
    {delta:1000, name:"GC Old"}
);
var gcCount2 = jolokia.metric(
    {type:"read", mbean:"java.lang:name=PS Scavenge,type=GarbageCollector", attribute:"CollectionCount"},
    {delta:1000, name:"GC Young"}
);

var agentRequest = jolokia.metric(
    {type:        "read", mbean:"Jolokia:J2EEApplication=none,J2EEServer=none,WebModule=//localhost/jolokia,j2eeType=Servlet,name=jolokia-agent",
        attribute:"requestCount"}, {name:"Jolokia", delta:10 * 1000});
var jenkinsRequest = jolokia.metric(
    {type:        "read", mbean:"Catalina:J2EEApplication=none,J2EEServer=none,WebModule=//localhost/jenkins,j2eeType=Servlet,name=Stapler",
        attribute:"requestCount"}, {name:"Jenkins", delta:10 * 1000});
var allRequests = jolokia.metric(
    function (resp) {
        var attrs = resp.value;
        var sum = 0;
        for (var key in attrs) {
            sum += attrs[key].requestCount;
        }
        return sum;
    },
    {type:        "read", mbean:"Catalina:j2eeType=Servlet,*",
        attribute:"requestCount"}, {name:"All", delta:10 * 1000});

//j4p.start(1000);

var colorsRed = ["#FDBE85", "#FEEDDE", "#FD8D3C", "#E6550D", "#A63603", "#FDBE85", "#FEEDDE", "#FD8D3C", "#E6550D", "#A63603" ],
    colorsGreen = [ "#E5F5F9", "#99D8C9", "#2CA25F", "#E5F5F9", "#99D8C9", "#2CA25F"],
    colorsBlue = [ "#ECE7F2", "#A6BDDB", "#2B8CBE", "#ECE7F2", "#A6BDDB", "#2B8CBE"];

// Created graphs
$(function () {
    d3.select("#memory").call(function (div) {

        div.append("div")
            .attr("class", "axis")
            .call(context.axis().orient("top"));

        div.selectAll(".horizon")
            .data([memory])
            .enter().append("div")
            .attr("class", "horizon")
            .call(
            context.horizon()
                .colors(colorsRed)
                .format(d3.format(".4p"))
        );
        div.selectAll(".horizon-gc")
            .data([gcCount2, gcCount])
            .enter().append("div")
            .attr("class", "horizon horizon-gc")
            .call(
            context.horizon().colors(colorsRed).height(10)
        );
        div.append("div")
            .attr("class", "rule")
            .call(context.rule());

    });

    d3.select("#request").call(function (div) {

        div.append("div")
            .attr("class", "axis")
            .call(context.axis().orient("top"));

        div.selectAll(".horizon")
            .data([agentRequest, jenkinsRequest, allRequests])
            .enter()
            .append("div")
            .attr("class", "horizon")
            .call(context.horizon()
            .format(d3.format("10d"))
            .colors(function (d, i) {
                return i == 3 ? colorsBlue : colorsGreen
            }));

        div.append("div")
            .attr("class", "rule")
            .call(context.rule());

    });

    // On mousemove, reposition the chart values to match the rule.
    context.on("focus", function (i) {
        d3.selectAll("#memory .value").style("right", i == null ? null : context.size() - i + "px");
        d3.selectAll("#request .value").style("right", i == null ? null : context.size() - i + "px");
    });

});

function gc() {
    j4p.request({type:"exec", mbean:"java.lang:type=Memory", operation:"gc"})
}
<|endoftext|>


<commit_before>/**
 * Created by Awesome on 1/30/2016.
 */

// use strict
'use strict';

// require dependencies
var mongorito = require('mongorito');

/**
 * build model
 */
class model extends mongorito.Model {
    /**
     * check attributes
     *
     * @private
     */
    _checkAttributes() {
        for (var key in this.attributes) {
            var attr = this.attributes[key];
            if (attr === Object(attr)) {
                if (attr.entity) {
                    //this.attributes[key] = require(global.appRoot + attr.entity).findById(attr.id);
                }
            }
        }
    }
}

/**
 * export default model class
 *
 * @type {model}
 */
module.exports = model;<commit_msg>log object constructor to console;<commit_after>/**
 * Created by Awesome on 1/30/2016.
 */

// use strict
'use strict';

// require dependencies
var mongorito = require('mongorito');

/**
 * build model
 */
class model extends mongorito.Model {
    constructor(props) {
        super(props);

        console.log(this.constructor);
    }
    /**
     * check attributes
     *
     * @private
     */
    _checkAttributes() {
        for (var key in this.attributes) {
            var attr = this.attributes[key];
            if (attr === Object(attr)) {
                if (attr.entity) {
                    //this.attributes[key] = require(global.appRoot + attr.entity).findById(attr.id);
                }
            }
        }
    }
}

/**
 * export default model class
 *
 * @type {model}
 */
module.exports = model;<|endoftext|>


<commit_before>var fs = require('fs');
var path = require('path');
var marked = require('marked');

var EXTENSION_HTML = ".html";
var EXTENSION_MARKDOWN_REGEX = /\.md$/g;
var OPTIONS_MARKED = {sanitize: true, tables: true};
var SWITCH_SOURCEDIR = "--sourceDir", SWITCH_DESTDIR = "--destDir", SWITCH_BASEURL = "--baseURL", SWITCH_OVERWRITE = "-overwrite";

var sourceDir, destinationDir, baseURL, overwrite;

process.argv.forEach(function(arg) {
	if (arg.indexOf(SWITCH_SOURCEDIR) === 0 && arg.indexOf("=") !== -1) {
		sourceDir = arg.substring(arg.indexOf("=") + 1);
	} else if (arg.indexOf(SWITCH_DESTDIR) === 0 && arg.indexOf("=") !== -1) {
		destinationDir = arg.substring(arg.indexOf("=") + 1);
//	} else if (arg.indexOf(SWITCH_BASEURL) === 0 && arg.indexOf("=") !== -1) {
//		baseURL = arg.substring(arg.indexOf("=") + 1);
	} else if (arg.indexOf(SWITCH_OVERWRITE) === 0) {
		overwrite = true;
	}
});

if (!sourceDir || !destinationDir) {
	console.log("Usage: node mdProcessor " + SWITCH_SOURCEDIR + "=<sourceDirectory> " + SWITCH_DESTDIR + "=<destinationDirectory> [" /* + SWITCH_BASEURL + "=<baseURL>]["*/ + SWITCH_OVERWRITE + "]");
	process.exit();
}

if (!fs.existsSync(sourceDir)) {
	console.log("Source directory does not exist: " + sourceDir);
	process.exit();
}

if (!fs.existsSync(destinationDir)) {
	var result = fs.mkdirSync(destinationDir);
	if (result) {
		console.log("Failed to create destination directory: " + destinationDir);
		console.log(result.toString());
		process.exit();
	}
}

var writeStat = fs.statSync(destinationDir);
var writeBlockSize = writeStat.blksize;

if (baseURL) {
	marked.InlineLexer.prototype.outputLink = baseURL;
}

var filenames = fs.readdirSync(sourceDir);
filenames.forEach(function(current) {
	if (EXTENSION_MARKDOWN_REGEX.test(current)) {
		var sourcePath = path.join(sourceDir, current);
		fs.open(sourcePath, "r", null, function(readErr, readFd) {
			if (readErr) {
				console.log("Failed to open file to read: " + sourcePath + "\n" + readErr.toString());
			} else {
				var readStat = fs.fstatSync(readFd);
				var readBlockSize = readStat.blksize;
				var fileSize = readStat.size;
				var inBuffer = new Buffer(fileSize);
				var totalReadCount = 0;
				do {
					var length = Math.min(readBlockSize, fileSize - totalReadCount);
					var readCount = fs.readSync(readFd, inBuffer, totalReadCount, length, null);
					if (!readCount) {
						break;
					}
					totalReadCount += readCount;
				} while (totalReadCount < fileSize);
				if (totalReadCount !== fileSize) {
					console.log("Failed to read the full content of file " + sourcePath);
				} else {
					var fileText = inBuffer.toString("utf8", 0, inBuffer.length);
					var markdownText = marked(fileText, OPTIONS_MARKED);
					if (!markdownText) {
						console.log("Failed during conversion of markdown to HTML file " + sourcePath);
					}
					var outBuffer = new Buffer(markdownText);
					var outputFilename = current.replace(EXTENSION_MARKDOWN_REGEX, EXTENSION_HTML);
					var destinationPath = path.join(destinationDir, outputFilename);
					fs.open(destinationPath, overwrite ? "w" : "wx", null, function(writeErr, writeFd) {
						if (writeErr) {
							console.log("Failed to open file to write: " + sourcePath + "\n" + writeErr.toString());
						} else {
							var totalWriteCount = 0;
							do {
								length = Math.min(writeBlockSize, outBuffer.length - totalWriteCount);
								var writeCount = fs.writeSync(writeFd, outBuffer, totalWriteCount, length, null);
								if (!writeCount) {
									console.log("0-length write, running away" + destinationPath);
									break;
								}
								totalWriteCount += writeCount;
							} while (totalWriteCount < outBuffer.length);
							if (totalWriteCount !== outBuffer.length) {
								console.log("Failed to write the full content of file " + destinationPath);
							} else {
								console.log("Wrote " + destinationPath);
							}
							fs.close(writeFd);
						}
					});
				}
				fs.close(readFd);
			}
		});
	}
});
<commit_msg>tweak output strings<commit_after>var fs = require('fs');
var path = require('path');
var marked = require('marked');

var EXTENSION_HTML = ".html";
var EXTENSION_MARKDOWN_REGEX = /\.md$/g;
var OPTIONS_MARKED = {sanitize: true, tables: true};
var SWITCH_SOURCEDIR = "--sourceDir", SWITCH_DESTDIR = "--destDir", SWITCH_BASEURL = "--baseURL", SWITCH_OVERWRITE = "-overwrite";

var sourceDir, destinationDir, baseURL, overwrite;

process.argv.forEach(function(arg) {
	if (arg.indexOf(SWITCH_SOURCEDIR) === 0 && arg.indexOf("=") !== -1) {
		sourceDir = arg.substring(arg.indexOf("=") + 1);
	} else if (arg.indexOf(SWITCH_DESTDIR) === 0 && arg.indexOf("=") !== -1) {
		destinationDir = arg.substring(arg.indexOf("=") + 1);
//	} else if (arg.indexOf(SWITCH_BASEURL) === 0 && arg.indexOf("=") !== -1) {
//		baseURL = arg.substring(arg.indexOf("=") + 1);
	} else if (arg.indexOf(SWITCH_OVERWRITE) === 0) {
		overwrite = true;
	}
});

if (!sourceDir || !destinationDir) {
	console.log("Usage: node mdProcessor " + SWITCH_SOURCEDIR + "=<sourceDirectory> " + SWITCH_DESTDIR + "=<destinationDirectory> [" /* + SWITCH_BASEURL + "=<baseURL>]["*/ + SWITCH_OVERWRITE + "]");
	process.exit();
}

if (!fs.existsSync(sourceDir)) {
	console.log("Source directory does not exist: " + sourceDir);
	process.exit();
}

if (!fs.existsSync(destinationDir)) {
	var result = fs.mkdirSync(destinationDir);
	if (result) {
		console.log("Failed to create destination directory: " + destinationDir);
		console.log(result.toString());
		process.exit();
	}
}

var writeStat = fs.statSync(destinationDir);
var writeBlockSize = writeStat.blksize;

if (baseURL) {
	marked.InlineLexer.prototype.outputLink = baseURL;
}

console.log("");
var filenames = fs.readdirSync(sourceDir);
filenames.forEach(function(current) {
	if (EXTENSION_MARKDOWN_REGEX.test(current)) {
		var sourcePath = path.join(sourceDir, current);
		fs.open(sourcePath, "r", null, function(readErr, readFd) {
			if (readErr) {
				console.log("Failed to open file to read: " + sourcePath + "\n" + readErr.toString());
			} else {
				var readStat = fs.fstatSync(readFd);
				var readBlockSize = readStat.blksize;
				var fileSize = readStat.size;
				var inBuffer = new Buffer(fileSize);
				var totalReadCount = 0;
				do {
					var length = Math.min(readBlockSize, fileSize - totalReadCount);
					var readCount = fs.readSync(readFd, inBuffer, totalReadCount, length, null);
					if (!readCount) {
						break;
					}
					totalReadCount += readCount;
				} while (totalReadCount < fileSize);
				if (totalReadCount !== fileSize) {
					console.log("Failed to read the full content of file " + sourcePath);
				} else {
					var fileText = inBuffer.toString("utf8", 0, inBuffer.length);
					var markdownText = marked(fileText, OPTIONS_MARKED);
					if (!markdownText) {
						console.log("Failed during conversion of markdown to HTML file " + sourcePath);
					}
					var outBuffer = new Buffer(markdownText);
					var outputFilename = current.replace(EXTENSION_MARKDOWN_REGEX, EXTENSION_HTML);
					var destinationPath = path.join(destinationDir, outputFilename);
					fs.open(destinationPath, overwrite ? "w" : "wx", null, function(writeErr, writeFd) {
						if (writeErr) {
							console.log("Failed to open file to write: " + sourcePath + "\n" + writeErr.toString());
						} else {
							var totalWriteCount = 0;
							do {
								length = Math.min(writeBlockSize, outBuffer.length - totalWriteCount);
								var writeCount = fs.writeSync(writeFd, outBuffer, totalWriteCount, length, null);
								if (!writeCount) {
									console.log("0-length write, running away" + destinationPath);
									break;
								}
								totalWriteCount += writeCount;
							} while (totalWriteCount < outBuffer.length);
							if (totalWriteCount !== outBuffer.length) {
								console.log("Failed to write the full content of file " + destinationPath);
							} else {
								console.log("-->Wrote " + destinationPath);
							}
							fs.close(writeFd);
						}
					});
				}
				fs.close(readFd);
			}
		});
	}
});
<|endoftext|>


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

const yargs = require('yargs')
const path = require('path')
const restApi = require(`../../src-shared/rest-api.js`)

// TODO how to handle relative pathing for things like properties file.
exports.zclPropertiesFile = path.join(
  __dirname,
  '../../zcl-builtin/silabs/zcl.json'
)
exports.genTemplateJsonFile = './test/gen-template/zigbee/gen-templates.json'
exports.httpPort = 9070
exports.studioHttpPort = 9000
exports.uiMode = restApi.uiMode.ZIGBEE
exports.embeddedMode = false
exports.noServer = false
exports.zapFile = null
exports.genResultFile = false

/**
 * Process the command line arguments and resets the state in this file
 * to the specified values.
 *
 * @export
 * @param {*} argv
 * @returns parsed argv object
 */
function processCommandLineArguments(argv) {
  console.log('YARG!!!')
  var ret = yargs
    .command('generate', 'Generate ZCL artifacts.')
    .command('selfCheck', 'Perform the self-check of the application.')
    .option('httpPort', {
      desc: 'Port used for the HTTP server',
      alias: 'p',
      type: 'number',
      default: exports.httpPort,
    })
    .option('studioHttpPort', {
      desc: "Port used for integration with Studio's UC Jetty server",
      type: 'number',
      default: exports.studioHttpPort,
    })
    .option('zapFile', {
      desc: 'input .zap file to read in.',
      alias: ['zap', 'in', 'i'],
      type: 'string',
      default: exports.zapFile,
    })
    .option('zclProperties', {
      desc: 'zcl.properties file to read in.',
      alias: ['zcl', 'z'],
      type: 'string',
      default: exports.zclPropertiesFile,
    })
    .option('generationTemplate', {
      desc: 'gen-template.json file to read in.',
      alias: ['gen', 'g'],
      type: 'string',
      default: exports.genTemplateJsonFile,
    })
    .option('uiMode', {
      desc: 'Mode of the UI to begin in. Options are: ZIGBEE',
      alias: 'ui',
      type: 'string',
      default: exports.uiMode,
    })
    .option('embeddedMode', {
      desc:
        'Boolean for when you want to embed purely the ZCL parts of the ZAP tool',
      alias: 'embed',
      type: 'boolean',
      default: exports.embeddedMode,
    })
    .option('noUi', {
      desc: "Don't show the main window when starting.",
    })
    .options('noServer', {
      desc:
        "Don't run the http server. You should probably also specify -noUi with this.",
      default: exports.noServer,
    })
    .options('genResultFile', {
      desc: 'If this option is present, then generate the result file.',
      default: exports.genResultFile,
    })
    .option('showUrl', {
      desc: 'Print out the URL that an external browser should use.',
    })
    .option('output', {
      desc: 'Specifying the output directory for generation',
      alias: ['out', 'o'],
      type: 'string',
    })
    .option('clearDb', {
      desc: 'Clear out the database and start with a new file.',
      type: 'string',
    })
    .usage('Usage: $0 <command> [options]')
    .help('h')
    .alias('h', 'help')
    .epilogue('For more information, see https://github.com/project-chip/zap')
    .wrap(null)
    .parse(argv)

  // Now populate exported variables with this.
  exports.zclPropertiesFile = ret.zclProperties
  exports.httpPort = ret.httpPort
  exports.studioHttpPort = ret.studioHttpPort
  exports.uiMode = ret.uiMode
  exports.genTemplateJsonFile = ret.generationTemplate
  exports.embeddedMode = ret.embeddedMode
  exports.noServer = ret.noServer
  exports.zapFile = ret.zapFile
  exports.genResultFile = ret.genResultFile
  return ret
}

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

const yargs = require('yargs')
const path = require('path')
const restApi = require(`../../src-shared/rest-api.js`)

// TODO how to handle relative pathing for things like properties file.
exports.zclPropertiesFile = path.join(
  __dirname,
  '../../zcl-builtin/silabs/zcl.json'
)
exports.genTemplateJsonFile = './test/gen-template/zigbee/gen-templates.json'
exports.httpPort = 9070
exports.studioHttpPort = 9000
exports.uiMode = restApi.uiMode.ZIGBEE
exports.embeddedMode = false
exports.noServer = false
exports.zapFile = null
exports.genResultFile = false

/**
 * Process the command line arguments and resets the state in this file
 * to the specified values.
 *
 * @export
 * @param {*} argv
 * @returns parsed argv object
 */
function processCommandLineArguments(argv) {
  var ret = yargs
    .command('generate', 'Generate ZCL artifacts.')
    .command('selfCheck', 'Perform the self-check of the application.')
    .option('httpPort', {
      desc: 'Port used for the HTTP server',
      alias: 'p',
      type: 'number',
      default: exports.httpPort,
    })
    .option('studioHttpPort', {
      desc: "Port used for integration with Studio's UC Jetty server",
      type: 'number',
      default: exports.studioHttpPort,
    })
    .option('zapFile', {
      desc: 'input .zap file to read in.',
      alias: ['zap', 'in', 'i'],
      type: 'string',
      default: exports.zapFile,
    })
    .option('zclProperties', {
      desc: 'zcl.properties file to read in.',
      alias: ['zcl', 'z'],
      type: 'string',
      default: exports.zclPropertiesFile,
    })
    .option('generationTemplate', {
      desc: 'gen-template.json file to read in.',
      alias: ['gen', 'g'],
      type: 'string',
      default: exports.genTemplateJsonFile,
    })
    .option('uiMode', {
      desc: 'Mode of the UI to begin in. Options are: ZIGBEE',
      alias: 'ui',
      type: 'string',
      default: exports.uiMode,
    })
    .option('embeddedMode', {
      desc:
        'Boolean for when you want to embed purely the ZCL parts of the ZAP tool',
      alias: 'embed',
      type: 'boolean',
      default: exports.embeddedMode,
    })
    .option('noUi', {
      desc: "Don't show the main window when starting.",
    })
    .options('noServer', {
      desc:
        "Don't run the http server. You should probably also specify -noUi with this.",
      default: exports.noServer,
    })
    .options('genResultFile', {
      desc: 'If this option is present, then generate the result file.',
      default: exports.genResultFile,
    })
    .option('showUrl', {
      desc: 'Print out the URL that an external browser should use.',
    })
    .option('output', {
      desc: 'Specifying the output directory for generation',
      alias: ['out', 'o'],
      type: 'string',
    })
    .option('clearDb', {
      desc: 'Clear out the database and start with a new file.',
      type: 'string',
    })
    .usage('Usage: $0 <command> [options]')
    .help('h')
    .alias('h', 'help')
    .epilogue('For more information, see https://github.com/project-chip/zap')
    .wrap(null)
    .parse(argv)

  // Now populate exported variables with this.
  exports.zclPropertiesFile = ret.zclProperties
  exports.httpPort = ret.httpPort
  exports.studioHttpPort = ret.studioHttpPort
  exports.uiMode = ret.uiMode
  exports.genTemplateJsonFile = ret.generationTemplate
  exports.embeddedMode = ret.embeddedMode
  exports.noServer = ret.noServer
  exports.zapFile = ret.zapFile
  exports.genResultFile = ret.genResultFile
  return ret
}

exports.processCommandLineArguments = processCommandLineArguments
<|endoftext|>


<commit_before>import { prefs } from '../core/prefs';
import * as editorDialog from './editor-dialog';
import * as dom from '../core/dom';
import * as func from '../core/func';
import * as color from '../core/color';

function getOrderedUsers ()  {
	return prefs.getCleanHighlightedUsers().then(users => func.sortBy(users, 'color'));
}

function open (options) {
	return editorDialog.open({
		id: 'EditHighlightedUsersDialog',
		title: 'Edit highlighted users',
		tableRowValueMap: user => [user.name, user.color],
		tpl: {
			formID: 'HighlightUserForm',
			tableID: 'ListOfHighlightedUsers',
			tableHead: [ 'Name', 'Color', 'Delete' ],
			notFoundTitle: 'No users added',
			fields: [{
				id: 'HighlightedUserName',
				label: 'Name',
				type: 'text',
				name: 'userName',
				value: ''
			}, {
				id: 'HighlightedUserColor',
				label: 'Color',
				type: 'color',
				name: 'userColor',
				value: options.huppercolor
			}]
		},

		formValueMap: ['userName', 'userColor'],

		get: getOrderedUsers,
		remove: prefs.removeHighlightedUser.bind(prefs),
		add: prefs.addHighlightedUser.bind(prefs),
		changeField: 'highlightusers'
	}).then(dialog => {
		let rndBtn = dom.createElem('button', [{name: 'type', value: 'button'}], ['button', 'random-color'], 'Random color');
		dialog.panel.querySelector('#HighlightedUserColor').parentNode.appendChild(rndBtn);
		dialog.panel.addEventListener('click', (e) => {
			let colorElem = dom.elemOrClosest('.color', e.target);

			if (colorElem) {
				e.preventDefault();
				dialog.panel.querySelector('#HighlightedUserColor').value = colorElem.querySelector('.color-text').textContent;

				return;
			}

			let randomColor = dom.elemOrClosest('.random-color', e.target);

			if (randomColor) {
				e.preventDefault();
				let newColor = color.getRandomColor();

				dialog.panel.querySelector('#HighlightedUserColor').value = newColor;
			}

		}, false);

		return dialog;
	}).then(dialog => {
		dialog.panel.addEventListener('click', (e) => {
			let td = dom.elemOrClosest('td', e.target);

			if (td && td === td.parentNode.querySelector('td')) {
				e.preventDefault();
				dialog.panel.querySelector('#HighlightedUserName').value = td.textContent.trim();
			}

		});

		return dialog;
	});
}

export { open };
<commit_msg>feat(options) Change add button text to update<commit_after>import { prefs } from '../core/prefs';
import * as editorDialog from './editor-dialog';
import * as dom from '../core/dom';
import * as func from '../core/func';
import * as color from '../core/color';

function getOrderedUsers ()  {
	return prefs.getCleanHighlightedUsers().then(users => func.sortBy(users, 'color'));
}

function dispatchUserNameChange (target) {
	const event = new Event('userNameChange', {
		'bubbles': true,
		'cancelable': true,
	});
	target.dispatchEvent(event);
}

function open (options) {
	return editorDialog.open({
		id: 'EditHighlightedUsersDialog',
		title: 'Edit highlighted users',
		tableRowValueMap: user => [user.name, user.color],
		tpl: {
			formID: 'HighlightUserForm',
			tableID: 'ListOfHighlightedUsers',
			tableHead: [ 'Name', 'Color', 'Delete' ],
			notFoundTitle: 'No users added',
			fields: [{
				id: 'HighlightedUserName',
				label: 'Name',
				type: 'text',
				name: 'userName',
				value: ''
			}, {
				id: 'HighlightedUserColor',
				label: 'Color',
				type: 'color',
				name: 'userColor',
				value: options.huppercolor
			}]
		},

		formValueMap: ['userName', 'userColor'],

		get: getOrderedUsers,
		remove: prefs.removeHighlightedUser.bind(prefs),
		add: prefs.addHighlightedUser.bind(prefs),
		changeField: 'highlightusers'
	}).then(dialog => {
		let rndBtn = dom.createElem('button', [{name: 'type', value: 'button'}], ['button', 'random-color'], 'Random color');
		dialog.panel.querySelector('#HighlightedUserColor').parentNode.appendChild(rndBtn);
		dialog.panel.addEventListener('click', (e) => {
			let colorElem = dom.elemOrClosest('.color', e.target);

			if (colorElem) {
				e.preventDefault();
				dialog.panel.querySelector('#HighlightedUserColor').value = colorElem.querySelector('.color-text').textContent;

				return;
			}

			let randomColor = dom.elemOrClosest('.random-color', e.target);

			if (randomColor) {
				e.preventDefault();
				let newColor = color.getRandomColor();

				dialog.panel.querySelector('#HighlightedUserColor').value = newColor;
			}

		}, false);

		return dialog;
	}).then(dialog => {
		dialog.panel.addEventListener('click', (e) => {
			let td = dom.elemOrClosest('td', e.target);

			if (td && td === td.parentNode.querySelector('td')) {
				e.preventDefault();
				const input = dialog.panel.querySelector('#HighlightedUserName');
				input.value = td.textContent.trim();
				dispatchUserNameChange(input);
			}
		});
		return dialog;
	}).then(dialog => {
		const onUserNameChange = (e) => {
			const value = e.target.value;
			getOrderedUsers().then(users => {
				const btn = dialog.panel.querySelector('.btn-cta');
				if (func.index(users, u => u.name === value) > -1) {
					btn.textContent = 'Update';
				} else {
					btn.textContent = 'Add';
				}
			});
		};

		const onValueChange = (e) => {
			if (dom.is('[name="userName"]', e.target)) {
				dispatchUserNameChange(e.target);
			}
		};
		dialog.panel.addEventListener('userNameChange', onUserNameChange);
		dialog.panel.addEventListener('input', onValueChange);
		dialog.panel.addEventListener('change', onValueChange);
	});
}

export { open };
<|endoftext|>


<commit_before>/**
* Controls the actions in the project header (make public/private, watch button,
* forking, etc.)
*/
'use strict';

var $ = require('jquery');
var $osf = require('js/osfHelpers');
var ko = require('knockout');
var bootbox = require('bootbox');
var Raven = require('raven-js');
require('bootstrap-editable');
require('knockout.punches');
ko.punches.enableAll();

var osfHelpers = require('js/osfHelpers');
var NodeActions = require('js/project.js');
var iconmap = require('js/iconmap');

/**
 * The ProjectViewModel, scoped to the project header.
 * @param {Object} data The parsed project data returned from the project's API url.
 */
var ProjectViewModel = function(data) {
    var self = this;
    self._id = data.node.id;
    self.apiUrl = data.node.api_url;
    self.dateCreated = new osfHelpers.FormattableDate(data.node.date_created);
    self.dateModified = new osfHelpers.FormattableDate(data.node.date_modified);
    self.dateForked = new osfHelpers.FormattableDate(data.node.forked_date);
    self.parent = data.parent_node;
    self.doi = ko.observable(data.node.identifiers.doi);
    self.ark = ko.observable(data.node.identifiers.ark);
    self.idCreationInProgress = ko.observable(false);
    self.watchedCount = ko.observable(data.node.watched_count);
    self.userIsWatching = ko.observable(data.user.is_watching);
    self.dateRegistered = new osfHelpers.FormattableDate(data.node.registered_date);
    self.inDashboard = ko.observable(data.node.in_dashboard);
    self.dashboard = data.user.dashboard_id;
    self.userCanEdit = data.user.can_edit;
    self.userPermissions = data.user.permissions;
    self.description = data.node.description;
    self.title = data.node.title;
    self.category = data.node.category;
    self.isRegistration = data.node.is_registration;
    self.user = data.user;
    self.nodeIsPublic = data.node.is_public;
    self.nodeType = data.node.node_type;
    self.instLogoPath = ko.observable('');

    if (data.node.institution) {
        $.ajax({
            'method': 'GET',
            'url': window.contextVars.apiV2Prefix + 'nodes/' + self._id + '/institution/',
        }).done(function (response) {
            self.instLogoPath(response.data.attributes.logopath);
        });
    }

    // The button text to display (e.g. "Watch" if not watching)
    self.watchButtonDisplay = ko.pureComputed(function() {
        return self.watchedCount().toString();
    });
    self.watchButtonAction = ko.pureComputed(function() {
        return self.userIsWatching() ? 'Unwatch' : 'Watch';
    });

    self.canBeOrganized = ko.pureComputed(function() {
        return !!(self.user.username && (self.nodeIsPublic || self.user.has_read_permissions));
    });

    // Add icon to title
    self.icon = '';
    var category = data.node.category_short;
    if (Object.keys(iconmap.componentIcons).indexOf(category) >=0 ){
        self.icon = iconmap.componentIcons[category];
    }
    else {
        self.icon = iconmap.projectIcons[category];
    }

    // Editable Title and Description
    if (self.userCanEdit) {
        var editableOptions = {
            type: 'text',
            pk: self._id,
            url: self.apiUrl + 'edit/',
            ajaxOptions: {
                type: 'POST',
                dataType: 'json',
                contentType: 'application/json'
            },
            params: function (params) {
                // Send JSON data
                return JSON.stringify(params);
            },
            success: function () {
                document.location.reload(true);
            },
            error: osfHelpers.handleEditableError,
            placement: 'bottom'
        };

        // TODO: Remove hardcoded selectors.
        $.fn.editable.defaults.mode = 'inline';
        $('#nodeTitleEditable').editable($.extend({}, editableOptions, {
            name: 'title',
            title: 'Edit Title',
            tpl: '<input type="text" maxlength="200">',
            validate: function (value) {
                if ($.trim(value) === '') {
                    return 'Title cannot be blank.';
                }
                else if(value.length > 200){
                    return 'Title cannot exceed 200 characters.';
                }
            }
        }));

        $('#nodeDescriptionEditable').editable($.extend({}, editableOptions, {
            name: 'description',
            title: 'Edit Description',
            emptytext: 'No description',
            emptyclass: 'text-muted'
        }));
    }

    /**
     * Add project to the Project Organizer.
     */
    self.addToDashboard = function() {
        $('#addDashboardFolder').tooltip('hide');
        self.inDashboard(true);
        var jsonData = {
            'toNodeID': self.dashboard,
            'pointerID': self._id
        };
        osfHelpers.postJSON('/api/v1/pointer/', jsonData)
            .fail(function(data) {
                self.inDashboard(false);
                osfHelpers.handleJSONError(data);
        });
    };
    /**
     * Remove project from the Project Organizer.
     */
    self.removeFromDashboard = function() {
        $('#removeDashboardFolder').tooltip('hide');
        self.inDashboard(false);
        var deleteUrl = '/api/v1/folder/' + self.dashboard + '/pointer/' + self._id;
        $.ajax({url: deleteUrl, type: 'DELETE'})
            .fail(function() {
                self.inDashboard(true);
                osfHelpers.growl('Error', 'The project could not be removed', 'danger');
        });
    };

    /**
     * Toggle the watch status for this project.
     */
    var watchUpdateInProgress = false;
    self.toggleWatch = function() {
        // When there is no watch-update in progress,
        // send POST request to node's watch API url and update the watch count
        if(!watchUpdateInProgress) {
            if (self.userIsWatching()) {
                self.watchedCount(self.watchedCount() - 1);
            } else {
                self.watchedCount(self.watchedCount() + 1);
            }
            watchUpdateInProgress = true;
            osfHelpers.postJSON(
                self.apiUrl + 'togglewatch/',
                {}
            ).done(function (data) {
                // Update watch count in DOM
                watchUpdateInProgress = false;
                self.userIsWatching(data.watched);
                self.watchedCount(data.watchCount);
            }).fail(
                osfHelpers.handleJSONError
            );
        }
    };

    self.forkNode = function() {
        NodeActions.forkNode();
    };

    self.hasIdentifiers = ko.pureComputed(function() {
        return !!(self.doi() && self.ark());
    });

    self.canCreateIdentifiers = ko.pureComputed(function() {
        return !self.hasIdentifiers() &&
            self.isRegistration &&
            self.nodeIsPublic &&
            self.userPermissions.indexOf('admin') !== -1;
    });

    self.doiUrl = ko.pureComputed(function() {
        return self.doi() ? 'http://ezid.cdlib.org/id/doi:' + self.doi() : null;
    });

    self.arkUrl = ko.pureComputed(function() {
        return self.ark() ? 'http://ezid.cdlib.org/id/ark:/' + self.ark() : null;
    });

    self.askCreateIdentifiers = function() {
        var self = this;
        bootbox.confirm({
            title: 'Create identifiers',
            message: '
' +
                'Are you sure you want to create a DOI and ARK for this ' +
                self.nodeType + '?',
            callback: function(confirmed) {
                if (confirmed) {
                    self.createIdentifiers();
                }
            },
            buttons:{
                confirm:{
                    label:'Create'
                }
            }
        });
    };

    self.createIdentifiers = function() {
        // Only show loading indicator for slow responses
        var timeout = setTimeout(function() {
            self.idCreationInProgress(true); // show loading indicator
        }, 500);
        var url = self.apiUrl + 'identifiers/';
        return $.post(
            url
        ).done(function(resp) {
            self.doi(resp.doi);
            self.ark(resp.ark);
        }).fail(function(xhr) {
            var message = 'We could not create the identifier at this time. ' +
                'The DOI/ARK acquisition service may be down right now. ' +
                'Please try again soon and/or contact ' +
                'support@osf.io';
            osfHelpers.growl('Error', message, 'danger');
            Raven.captureMessage('Could not create identifiers', {url: url, status: xhr.status});
        }).always(function() {
            clearTimeout(timeout);
            self.idCreationInProgress(false); // hide loading indicator
        });
    };

};

////////////////
// Public API //
////////////////

var defaults = {
    removeCss: '.user-quickedit'
};

function NodeControl (selector, data, options) {
    var self = this;
    self.selector = selector;
    self.$element = $(self.selector);
    self.data = data;
    self.viewModel = new ProjectViewModel(self.data);
    self.options = $.extend({}, defaults, options);
    self.init();
}

NodeControl.prototype.init = function() {
    var self = this;
    osfHelpers.applyBindings(self.viewModel, this.selector);
};

module.exports = {
    _ProjectViewModel: ProjectViewModel,
    NodeControl: NodeControl
};
<commit_msg>Fix logo_path<commit_after>/**
* Controls the actions in the project header (make public/private, watch button,
* forking, etc.)
*/
'use strict';

var $ = require('jquery');
var $osf = require('js/osfHelpers');
var ko = require('knockout');
var bootbox = require('bootbox');
var Raven = require('raven-js');
require('bootstrap-editable');
require('knockout.punches');
ko.punches.enableAll();

var osfHelpers = require('js/osfHelpers');
var NodeActions = require('js/project.js');
var iconmap = require('js/iconmap');

/**
 * The ProjectViewModel, scoped to the project header.
 * @param {Object} data The parsed project data returned from the project's API url.
 */
var ProjectViewModel = function(data) {
    var self = this;
    self._id = data.node.id;
    self.apiUrl = data.node.api_url;
    self.dateCreated = new osfHelpers.FormattableDate(data.node.date_created);
    self.dateModified = new osfHelpers.FormattableDate(data.node.date_modified);
    self.dateForked = new osfHelpers.FormattableDate(data.node.forked_date);
    self.parent = data.parent_node;
    self.doi = ko.observable(data.node.identifiers.doi);
    self.ark = ko.observable(data.node.identifiers.ark);
    self.idCreationInProgress = ko.observable(false);
    self.watchedCount = ko.observable(data.node.watched_count);
    self.userIsWatching = ko.observable(data.user.is_watching);
    self.dateRegistered = new osfHelpers.FormattableDate(data.node.registered_date);
    self.inDashboard = ko.observable(data.node.in_dashboard);
    self.dashboard = data.user.dashboard_id;
    self.userCanEdit = data.user.can_edit;
    self.userPermissions = data.user.permissions;
    self.description = data.node.description;
    self.title = data.node.title;
    self.category = data.node.category;
    self.isRegistration = data.node.is_registration;
    self.user = data.user;
    self.nodeIsPublic = data.node.is_public;
    self.nodeType = data.node.node_type;
    self.instLogoPath = ko.observable('');

    if (data.node.institution) {
        $.ajax({
            'method': 'GET',
            'url': window.contextVars.apiV2Prefix + 'nodes/' + self._id + '/institution/',
        }).done(function (response) {
            self.instLogoPath(response.data.attributes.logo_path);
        });
    }

    // The button text to display (e.g. "Watch" if not watching)
    self.watchButtonDisplay = ko.pureComputed(function() {
        return self.watchedCount().toString();
    });
    self.watchButtonAction = ko.pureComputed(function() {
        return self.userIsWatching() ? 'Unwatch' : 'Watch';
    });

    self.canBeOrganized = ko.pureComputed(function() {
        return !!(self.user.username && (self.nodeIsPublic || self.user.has_read_permissions));
    });

    // Add icon to title
    self.icon = '';
    var category = data.node.category_short;
    if (Object.keys(iconmap.componentIcons).indexOf(category) >=0 ){
        self.icon = iconmap.componentIcons[category];
    }
    else {
        self.icon = iconmap.projectIcons[category];
    }

    // Editable Title and Description
    if (self.userCanEdit) {
        var editableOptions = {
            type: 'text',
            pk: self._id,
            url: self.apiUrl + 'edit/',
            ajaxOptions: {
                type: 'POST',
                dataType: 'json',
                contentType: 'application/json'
            },
            params: function (params) {
                // Send JSON data
                return JSON.stringify(params);
            },
            success: function () {
                document.location.reload(true);
            },
            error: osfHelpers.handleEditableError,
            placement: 'bottom'
        };

        // TODO: Remove hardcoded selectors.
        $.fn.editable.defaults.mode = 'inline';
        $('#nodeTitleEditable').editable($.extend({}, editableOptions, {
            name: 'title',
            title: 'Edit Title',
            tpl: '<input type="text" maxlength="200">',
            validate: function (value) {
                if ($.trim(value) === '') {
                    return 'Title cannot be blank.';
                }
                else if(value.length > 200){
                    return 'Title cannot exceed 200 characters.';
                }
            }
        }));

        $('#nodeDescriptionEditable').editable($.extend({}, editableOptions, {
            name: 'description',
            title: 'Edit Description',
            emptytext: 'No description',
            emptyclass: 'text-muted'
        }));
    }

    /**
     * Add project to the Project Organizer.
     */
    self.addToDashboard = function() {
        $('#addDashboardFolder').tooltip('hide');
        self.inDashboard(true);
        var jsonData = {
            'toNodeID': self.dashboard,
            'pointerID': self._id
        };
        osfHelpers.postJSON('/api/v1/pointer/', jsonData)
            .fail(function(data) {
                self.inDashboard(false);
                osfHelpers.handleJSONError(data);
        });
    };
    /**
     * Remove project from the Project Organizer.
     */
    self.removeFromDashboard = function() {
        $('#removeDashboardFolder').tooltip('hide');
        self.inDashboard(false);
        var deleteUrl = '/api/v1/folder/' + self.dashboard + '/pointer/' + self._id;
        $.ajax({url: deleteUrl, type: 'DELETE'})
            .fail(function() {
                self.inDashboard(true);
                osfHelpers.growl('Error', 'The project could not be removed', 'danger');
        });
    };

    /**
     * Toggle the watch status for this project.
     */
    var watchUpdateInProgress = false;
    self.toggleWatch = function() {
        // When there is no watch-update in progress,
        // send POST request to node's watch API url and update the watch count
        if(!watchUpdateInProgress) {
            if (self.userIsWatching()) {
                self.watchedCount(self.watchedCount() - 1);
            } else {
                self.watchedCount(self.watchedCount() + 1);
            }
            watchUpdateInProgress = true;
            osfHelpers.postJSON(
                self.apiUrl + 'togglewatch/',
                {}
            ).done(function (data) {
                // Update watch count in DOM
                watchUpdateInProgress = false;
                self.userIsWatching(data.watched);
                self.watchedCount(data.watchCount);
            }).fail(
                osfHelpers.handleJSONError
            );
        }
    };

    self.forkNode = function() {
        NodeActions.forkNode();
    };

    self.hasIdentifiers = ko.pureComputed(function() {
        return !!(self.doi() && self.ark());
    });

    self.canCreateIdentifiers = ko.pureComputed(function() {
        return !self.hasIdentifiers() &&
            self.isRegistration &&
            self.nodeIsPublic &&
            self.userPermissions.indexOf('admin') !== -1;
    });

    self.doiUrl = ko.pureComputed(function() {
        return self.doi() ? 'http://ezid.cdlib.org/id/doi:' + self.doi() : null;
    });

    self.arkUrl = ko.pureComputed(function() {
        return self.ark() ? 'http://ezid.cdlib.org/id/ark:/' + self.ark() : null;
    });

    self.askCreateIdentifiers = function() {
        var self = this;
        bootbox.confirm({
            title: 'Create identifiers',
            message: '
' +
                'Are you sure you want to create a DOI and ARK for this ' +
                self.nodeType + '?',
            callback: function(confirmed) {
                if (confirmed) {
                    self.createIdentifiers();
                }
            },
            buttons:{
                confirm:{
                    label:'Create'
                }
            }
        });
    };

    self.createIdentifiers = function() {
        // Only show loading indicator for slow responses
        var timeout = setTimeout(function() {
            self.idCreationInProgress(true); // show loading indicator
        }, 500);
        var url = self.apiUrl + 'identifiers/';
        return $.post(
            url
        ).done(function(resp) {
            self.doi(resp.doi);
            self.ark(resp.ark);
        }).fail(function(xhr) {
            var message = 'We could not create the identifier at this time. ' +
                'The DOI/ARK acquisition service may be down right now. ' +
                'Please try again soon and/or contact ' +
                'support@osf.io';
            osfHelpers.growl('Error', message, 'danger');
            Raven.captureMessage('Could not create identifiers', {url: url, status: xhr.status});
        }).always(function() {
            clearTimeout(timeout);
            self.idCreationInProgress(false); // hide loading indicator
        });
    };

};

////////////////
// Public API //
////////////////

var defaults = {
    removeCss: '.user-quickedit'
};

function NodeControl (selector, data, options) {
    var self = this;
    self.selector = selector;
    self.$element = $(self.selector);
    self.data = data;
    self.viewModel = new ProjectViewModel(self.data);
    self.options = $.extend({}, defaults, options);
    self.init();
}

NodeControl.prototype.init = function() {
    var self = this;
    osfHelpers.applyBindings(self.viewModel, this.selector);
};

module.exports = {
    _ProjectViewModel: ProjectViewModel,
    NodeControl: NodeControl
};
<|endoftext|>