{"action":"created","comment":{"url":"https://api.github.com/repos/adaptlearning/adapt_authoring/pulls/comments/38553976","id":38553976,"diff_hunk":"@@ -0,0 +1,338 @@\n+var bower = require('bower');\n+var async = require('async');\n+var rimraf = require('rimraf');\n+var semver = require('semver');\n+var logger = require('./logger');\n+var database = require('./database');\n+var configuration = require('./configuration');\n+var ncp = require('ncp').ncp;\n+var mkdirp = require('mkdirp');\n+var fs = require('fs');\n+var path = require('path');\n+var _ = require('underscore');\n+var bowerOptions = require('../plugins/content/bower/defaults.json');\n+\n+/*\n+ * CONSTANTS\n+ */\n+var MODNAME = 'bowermanager',\n+    WAITFOR = 'contentmanager';\n+    \n+var BowerManager = function() {\n+  \n+}\n+\n+BowerManager.prototype.extractPackageInfo = function(plugin, pkgMeta, schema) {\n+  // Build package info.\n+  var info = {\n+    name: pkgMeta.name,\n+    displayName: pkgMeta.displayName,\n+    description: pkgMeta.description,\n+    version: pkgMeta.version,\n+    framework: pkgMeta.framework ? pkgMeta.framework : null,\n+    isLocalPackage: pkgMeta.isLocalPackage ? pkgMeta.isLocalPackage : false,\n+    properties: schema.properties,\n+    globals: schema.globals ? schema.globals : null\n+  };\n+\n+  if (pkgMeta.assetFields) {\n+    info.assetFields = pkgMeta.assetFields;\n+  }\n+\n+  // Set the type and package id for the package.\n+  info[plugin.getModelName()] = pkgMeta[plugin.getModelName()];\n+\n+  // Set extra properties.\n+  plugin.extra && plugin.extra.forEach(function (key) {\n+    if (pkgMeta[key]) {\n+      info[key] = pkgMeta[key];\n+    }\n+  });\n+\n+  return info;\n+}\n+\n+/**\n+ * Installs a specified Adapt plugin.\n+ * @param {string} pluginName - The name of the Adapt plugin\n+ * @param {string} pluginVersion - The semantic version or branch name to install, for latest use '*'\n+ * @param {function} callback - Callback function\n+ */\n+BowerManager.prototype.installPlugin = function(pluginName, pluginVersion, callback) {\n+  var self = this;\n+  \n+  // Formulate the package name.\n+  var packageName = (pluginVersion == '*')\n+    ? pluginName\n+    : pluginName + '#' + pluginVersion;\n+  \n+  // Interrogate the version.json file on installation as we cannot rely on including it via a 'require' call.\n+  fs.readFile(path.join(configuration.getConfig('root'), 'version.json'), 'utf8', function(err, version) {\n+    // Ensure the JSON is parsed. \n+    version = JSON.parse(version);\n+    \n+    if (err) {\n+      logger.log('error', err);\n+      return callback(err);\n+    }\n+  \n+    // Clear the bower cache for this plugin.\n+    rimraf(path.join(bowerOptions.directory, pluginName), function (err) {\n+      if (err) {\n+        logger.log('error', err);\n+        return callback(err);\n+      }\n+    \n+      // Query bower to verify that the specified plugin exists.\n+      bower.commands.search(packageName, bowerOptions)\n+        .on('error', callback)\n+        .on('end', function (results) { \n+          if (!results) {\n+            logger.log('warn', 'Plugin ' + packageName + ' not found!');\n+            return callback('Plugin ' + packageName + ' not found!');\n+          }\n+        \n+        // The plugin exists -- remove any fuzzy matches, e.g. adapt-contrib-assessment would \n+        // also bring in adapt-contrib-assessmentResults, etc.\n+        var bowerPackage = _.findWhere(results, {name: packageName});\n+ \n+        // Install the plugin from bower.\n+        bower.commands.install([bowerPackage.name], { save: true }, bowerOptions)\n+          .on('error', function(err) {\n+            logger.log('error', err);\n+            return callback(err);\n+          })\n+          .on('end', function (packageInfo) { \n+            // Unfortunately this is required as the only way to identify the type\n+            // of a plugin is to check for the presence of the property which \n+            // actually indicates its type. :-/\n+            var pluginType = packageInfo[pluginName].pkgMeta.hasOwnProperty('component') \n+              ? 'component'\n+              : packageInfo[pluginName].pkgMeta.hasOwnProperty('extension')\n+                ? 'extension'\n+                : packageInfo[pluginName].pkgMeta.hasOwnProperty('menu')\n+                  ? 'menu'\n+                  : packageInfo[pluginName].pkgMeta.hasOwnProperty('theme') \n+                    ? 'theme' : ''\n+            \n+            if (pluginType == '') {\n+              logger.log('error', 'Unable to identify pluginType for ' + packageName);\n+              return callback('Unable to identify pluginType for ' + packageName);\n+            }\n+                              \n+            app.contentmanager.getContentPlugin(pluginType, function(err, plugin) {\n+              if (err) {\n+                return callback(err);\n+              }\n+\n+              if (packageInfo[pluginName].pkgMeta.framework) {\n+                // If the plugin defines a framework, ensure that it is compatible\n+                if (semver.satisfies(semver.clean(version.adapt_framework), packageInfo[pluginName].pkgMeta.framework)) {\n+                  self.importPackage(plugin, packageInfo[pluginName], bowerOptions, callback);\n+                } else {\n+                  logger.log('error', 'Unable to install ' + packageInfo[pluginName].pkgMeta.name + '(' + packageInfo[pluginName].framework + ') as it is not supported in the current version of of the Adapt framework (' +  version.adapt_framework + ')');\n+                  return callback('Unable to install ' + packageInfo[pluginName].pkgMeta.name + ' as it is not supported in the current version of of the Adapt framework');\n+                }\n+              } else {\n+                self.importPackage(plugin, packageInfo[pluginName], bowerOptions, callback);   \n+              }\n+            });\n+          });\n+      });\n+    });  \n+  });\n+}\n+\n+/**\n+ * adds a new package to the system - fired after bower\n+ * has installed to the cache\n+ *\n+ * @param {object} plugin - bowerConfig object for a bower plugin type\n+ * @param {object} packageInfo - the bower package info retrieved during install\n+ * @param {object} options\n+ * @param {callback} callback\n+ */\n+BowerManager.prototype.importPackage = function (plugin, packageInfo, options, callback) {\n+  // Shuffle params.\n+  if ('function' === typeof options) {\n+    callback = options;\n+    options = {\n+      strict: false\n+    };\n+  } \n+\n+  var self = this;\n+  var pkgMeta = packageInfo.pkgMeta;\n+  var schemaPath = path.join(packageInfo.canonicalDir, options._adaptSchemaFile);\n+  \n+  fs.exists(schemaPath, function (exists) {\n+    if (!exists) {\n+      if (options.strict) {\n+        return callback('Package does not contain a schema');\n+      }\n+\n+      logger.log('warn', 'ignoring package with no schema: ' + pkgMeta.name);\n+      return callback(null);\n+    }\n+\n+    fs.readFile(schemaPath, function (err, data) {\n+      var schema = false;\n+      if (err) {\n+        if (options.strict) {\n+          return callback('Failed to parse schema for package ' + pkgMeta.name);\n+        }\n+\n+        logger.log('error', 'failed to parse schema for ' + pkgMeta.name, err);\n+        return callback(null);\n+      }\n+\n+      try {\n+        schema = JSON.parse(data);\n+      } catch (e) {\n+        if (options.strict) {\n+          return callback('Failed to parse schema for package ' + pkgMeta.name);\n+        }\n+\n+        logger.log('error', 'failed to parse schema for ' + pkgMeta.name, e);\n+        return callback(null);\n+      }\n+      \n+      // Build a path to the destination working folder.\n+      var destination = path.join(configuration.tempDir, configuration.getConfig('masterTenantID'), 'adapt_framework', 'src', plugin.bowerConfig.srcLocation, pkgMeta.name);\n+      \n+      // Remove whatever version of the plugin is there already.\n+      rimraf(destination, function(err) {\n+        if (err) {\n+          return logger.log('error', err);\n+        }\n+\n+        // Re-create the plugin folder.\n+        mkdirp(destination, function (err) {\n+          if (err) {\n+            return logger.log('error', err);\n+          }\n+\n+          // Move from the bower cache to the working directory.          \n+          ncp(packageInfo.canonicalDir, destination, function (err) {\n+            if (err) {\n+              // Don't double call callback.\n+              return logger.log('error', err);\n+            }\n+\n+            // Copy the plugin into place.\n+            ncp(packageInfo.canonicalDir, destination, function (err) {\n+              if (err) {\n+                return logger.log('error', err);\n+              }\n+\n+              logger.log('info', 'Successfully copied ' + pkgMeta.name + ' to ' + destination);\n+                        \n+              // Build the package information.\n+              var package = self.extractPackageInfo(plugin, pkgMeta, schema);\n+              var db = app.db;\n+              var pluginString = package.name + ' (v' + package.version + ')';\n+\n+              // Add the package to the collection.          \n+              // Check if a plugin with this name and version already exists.\n+              db.retrieve(plugin.getPluginType(), { name: package.name, version: package.version }, function (err, results) {\n+                if (err) {\n+                  logger.log('error', err);\n+                  return callback(err);\n+                }\n+                  \n+                if (results && results.length !== 0) {\n+                  // Don't add a duplicate.\n+                  if (options.strict) {\n+                    return callback(\"Can't add \" + pluginString + \": verion already exists\");\n+                  }\n+                  \n+                  return callback(null);\n+                }\n+                \n+                // Add the new plugin.\n+                db.create(plugin.getPluginType(), package, function (err, newPlugin) {\n+                  if (err) {                              \n+                    logger.log('error', err);\n+                    \n+                    if (options.strict) {\n+                      return callback(err);\n+                    }\n+        \n+                    logger.log('error', 'Failed to add package: ' + pluginString, err);\n+                    return callback(null);\n+                  }\n+          \n+                  logger.log('info', 'Added package: ' + pluginString);\n+          \n+                  // Retrieve any older versions of the plugin.\n+                  db.retrieve(plugin.getPluginType(), { name: package.name, version: { $ne: newPlugin.version } }, function (err, results) {\n+                    if (err) {\n+                      // strictness doesn't matter at this point\n+                      logger.log('error', 'Failed to retrieve previous packages: ' + err.message, err);\n+                    }\n+          \n+                    // Remove older versions of this plugin.\n+                    db.destroy(plugin.getPluginType(), { name: package.name, version: { $ne: newPlugin.version } }, function (err) { \n+                      if (err) {\n+                        logger.log('error', err);\n+                        return callback(err);\n+                      }\n+      \n+                      logger.log('info', 'Successfully removed versions of ' + package.name + ' (' + plugin.getPluginType() + ') older than ' + newPlugin.version);\n+      \n+                      return callback(null, newPlugin);\n+                    }); // Remove older versions of the plugin.\n+                  }); // Retrieve older versions of the plugin.\n+                }); // Add the new plugin.\n+              }); // Check if a plugin with this name and version already exists.\n+            });\n+          });\n+        });\n+      });\n+    });\n+  });\n+}\n+\n+exports = module.exports = {\n+\n+  // expose the bower manager constructor\n+  BowerManager : BowerManager,\n+\n+  /**\n+   * preload function\n+   *\n+   * @param {object} app - AdaptBuilder instance\n+   * @return {object} preloader - an AdaptBuilder ModulePreloader\n+   */\n+  preload : function (app) {\n+    var preloader = new app.ModulePreloader(app, MODNAME, { events: this.preloadHandle(app, new BowerManager()) });\n+    return preloader;\n+  },\n+\n+  /**\n+   * Event handler for preload events\n+   *\n+   * @param {object} app - Server instance\n+   * @param {object} instance - Instance of this module\n+   * @return {object} hash map of events and handlers\n+   */\n+  preloadHandle : function (app, instance){\n+\n+    return {\n+        preload : function(){\n+          var preloader = this;\n+          preloader.emit('preloadChange', MODNAME, app.preloadConstants.WAITING);\n+        },\n+\n+        moduleLoaded : function(modloaded){\n+          var preloader = this;\n+\n+           //is the module that loaded this modules requirement\n+          if (modloaded === WAITFOR) {\n+            app.bowermanager = instance;\n+            preloader.emit('preloadChange', MODNAME, app.preloadConstants.COMPLETE);\n+          }\n+        }\n+      };\n+  }\n+};","path":"lib/bowermanager.js","position":338,"original_position":338,"commit_id":"17c8ad27778921b5523d581912f2d307af6d14ec","original_commit_id":"17c8ad27778921b5523d581912f2d307af6d14ec","user":{"login":"brian-learningpool","id":1327471,"avatar_url":"https://avatars.githubusercontent.com/u/1327471?v=3","gravatar_id":"","url":"https://api.github.com/users/brian-learningpool","html_url":"https://github.com/brian-learningpool","followers_url":"https://api.github.com/users/brian-learningpool/followers","following_url":"https://api.github.com/users/brian-learningpool/following{/other_user}","gists_url":"https://api.github.com/users/brian-learningpool/gists{/gist_id}","starred_url":"https://api.github.com/users/brian-learningpool/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/brian-learningpool/subscriptions","organizations_url":"https://api.github.com/users/brian-learningpool/orgs","repos_url":"https://api.github.com/users/brian-learningpool/repos","events_url":"https://api.github.com/users/brian-learningpool/events{/privacy}","received_events_url":"https://api.github.com/users/brian-learningpool/received_events","type":"User","site_admin":false},"body":"In time, yes.  I was going to come back to that  This would impact the plugin upload functionality and the initial install, etc. which is beyond what I was originally trying to resolve.","created_at":"2015-09-02T16:32:30Z","updated_at":"2015-09-02T16:32:30Z","html_url":"https://github.com/adaptlearning/adapt_authoring/pull/801#discussion_r38553976","pull_request_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/pulls/801","_links":{"self":{"href":"https://api.github.com/repos/adaptlearning/adapt_authoring/pulls/comments/38553976"},"html":{"href":"https://github.com/adaptlearning/adapt_authoring/pull/801#discussion_r38553976"},"pull_request":{"href":"https://api.github.com/repos/adaptlearning/adapt_authoring/pulls/801"}}},"pull_request":{"url":"https://api.github.com/repos/adaptlearning/adapt_authoring/pulls/801","id":44009006,"html_url":"https://github.com/adaptlearning/adapt_authoring/pull/801","diff_url":"https://github.com/adaptlearning/adapt_authoring/pull/801.diff","patch_url":"https://github.com/adaptlearning/adapt_authoring/pull/801.patch","issue_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/issues/801","number":801,"state":"open","locked":false,"title":"Issue/788","user":{"login":"brian-learningpool","id":1327471,"avatar_url":"https://avatars.githubusercontent.com/u/1327471?v=3","gravatar_id":"","url":"https://api.github.com/users/brian-learningpool","html_url":"https://github.com/brian-learningpool","followers_url":"https://api.github.com/users/brian-learningpool/followers","following_url":"https://api.github.com/users/brian-learningpool/following{/other_user}","gists_url":"https://api.github.com/users/brian-learningpool/gists{/gist_id}","starred_url":"https://api.github.com/users/brian-learningpool/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/brian-learningpool/subscriptions","organizations_url":"https://api.github.com/users/brian-learningpool/orgs","repos_url":"https://api.github.com/users/brian-learningpool/repos","events_url":"https://api.github.com/users/brian-learningpool/events{/privacy}","received_events_url":"https://api.github.com/users/brian-learningpool/received_events","type":"User","site_admin":false},"body":"Adds support for refreshing core plugins as part of the upgrade.","created_at":"2015-09-02T15:53:08Z","updated_at":"2015-09-02T16:32:30Z","closed_at":null,"merged_at":null,"merge_commit_sha":"f7a44e70e42f5cdf20b62909139425e38385ef20","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/pulls/801/commits","review_comments_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/pulls/801/comments","review_comment_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/pulls/comments{/number}","comments_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/issues/801/comments","statuses_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/statuses/17c8ad27778921b5523d581912f2d307af6d14ec","head":{"label":"adaptlearning:issue/788","ref":"issue/788","sha":"17c8ad27778921b5523d581912f2d307af6d14ec","user":{"login":"adaptlearning","id":5629464,"avatar_url":"https://avatars.githubusercontent.com/u/5629464?v=3","gravatar_id":"","url":"https://api.github.com/users/adaptlearning","html_url":"https://github.com/adaptlearning","followers_url":"https://api.github.com/users/adaptlearning/followers","following_url":"https://api.github.com/users/adaptlearning/following{/other_user}","gists_url":"https://api.github.com/users/adaptlearning/gists{/gist_id}","starred_url":"https://api.github.com/users/adaptlearning/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/adaptlearning/subscriptions","organizations_url":"https://api.github.com/users/adaptlearning/orgs","repos_url":"https://api.github.com/users/adaptlearning/repos","events_url":"https://api.github.com/users/adaptlearning/events{/privacy}","received_events_url":"https://api.github.com/users/adaptlearning/received_events","type":"Organization","site_admin":false},"repo":{"id":13562581,"name":"adapt_authoring","full_name":"adaptlearning/adapt_authoring","owner":{"login":"adaptlearning","id":5629464,"avatar_url":"https://avatars.githubusercontent.com/u/5629464?v=3","gravatar_id":"","url":"https://api.github.com/users/adaptlearning","html_url":"https://github.com/adaptlearning","followers_url":"https://api.github.com/users/adaptlearning/followers","following_url":"https://api.github.com/users/adaptlearning/following{/other_user}","gists_url":"https://api.github.com/users/adaptlearning/gists{/gist_id}","starred_url":"https://api.github.com/users/adaptlearning/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/adaptlearning/subscriptions","organizations_url":"https://api.github.com/users/adaptlearning/orgs","repos_url":"https://api.github.com/users/adaptlearning/repos","events_url":"https://api.github.com/users/adaptlearning/events{/privacy}","received_events_url":"https://api.github.com/users/adaptlearning/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/adaptlearning/adapt_authoring","description":"Source code for the Adapt Authoring tool","fork":false,"url":"https://api.github.com/repos/adaptlearning/adapt_authoring","forks_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/forks","keys_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/keys{/key_id}","collaborators_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/teams","hooks_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/hooks","issue_events_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/issues/events{/number}","events_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/events","assignees_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/assignees{/user}","branches_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/branches{/branch}","tags_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/tags","blobs_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/git/refs{/sha}","trees_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/git/trees{/sha}","statuses_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/statuses/{sha}","languages_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/languages","stargazers_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/stargazers","contributors_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/contributors","subscribers_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/subscribers","subscription_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/subscription","commits_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/commits{/sha}","git_commits_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/git/commits{/sha}","comments_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/comments{/number}","issue_comment_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/issues/comments{/number}","contents_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/contents/{+path}","compare_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/compare/{base}...{head}","merges_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/merges","archive_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/downloads","issues_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/issues{/number}","pulls_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/pulls{/number}","milestones_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/milestones{/number}","notifications_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/labels{/name}","releases_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/releases{/id}","created_at":"2013-10-14T13:52:24Z","updated_at":"2015-09-02T07:12:32Z","pushed_at":"2015-09-02T16:06:53Z","git_url":"git://github.com/adaptlearning/adapt_authoring.git","ssh_url":"git@github.com:adaptlearning/adapt_authoring.git","clone_url":"https://github.com/adaptlearning/adapt_authoring.git","svn_url":"https://github.com/adaptlearning/adapt_authoring","homepage":null,"size":43498,"stargazers_count":45,"watchers_count":45,"language":"JavaScript","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":38,"mirror_url":null,"open_issues_count":104,"forks":38,"open_issues":104,"watchers":45,"default_branch":"master"}},"base":{"label":"adaptlearning:develop","ref":"develop","sha":"ef76899de616897603473f89d9e87e163cc878ff","user":{"login":"adaptlearning","id":5629464,"avatar_url":"https://avatars.githubusercontent.com/u/5629464?v=3","gravatar_id":"","url":"https://api.github.com/users/adaptlearning","html_url":"https://github.com/adaptlearning","followers_url":"https://api.github.com/users/adaptlearning/followers","following_url":"https://api.github.com/users/adaptlearning/following{/other_user}","gists_url":"https://api.github.com/users/adaptlearning/gists{/gist_id}","starred_url":"https://api.github.com/users/adaptlearning/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/adaptlearning/subscriptions","organizations_url":"https://api.github.com/users/adaptlearning/orgs","repos_url":"https://api.github.com/users/adaptlearning/repos","events_url":"https://api.github.com/users/adaptlearning/events{/privacy}","received_events_url":"https://api.github.com/users/adaptlearning/received_events","type":"Organization","site_admin":false},"repo":{"id":13562581,"name":"adapt_authoring","full_name":"adaptlearning/adapt_authoring","owner":{"login":"adaptlearning","id":5629464,"avatar_url":"https://avatars.githubusercontent.com/u/5629464?v=3","gravatar_id":"","url":"https://api.github.com/users/adaptlearning","html_url":"https://github.com/adaptlearning","followers_url":"https://api.github.com/users/adaptlearning/followers","following_url":"https://api.github.com/users/adaptlearning/following{/other_user}","gists_url":"https://api.github.com/users/adaptlearning/gists{/gist_id}","starred_url":"https://api.github.com/users/adaptlearning/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/adaptlearning/subscriptions","organizations_url":"https://api.github.com/users/adaptlearning/orgs","repos_url":"https://api.github.com/users/adaptlearning/repos","events_url":"https://api.github.com/users/adaptlearning/events{/privacy}","received_events_url":"https://api.github.com/users/adaptlearning/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/adaptlearning/adapt_authoring","description":"Source code for the Adapt Authoring tool","fork":false,"url":"https://api.github.com/repos/adaptlearning/adapt_authoring","forks_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/forks","keys_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/keys{/key_id}","collaborators_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/teams","hooks_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/hooks","issue_events_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/issues/events{/number}","events_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/events","assignees_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/assignees{/user}","branches_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/branches{/branch}","tags_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/tags","blobs_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/git/refs{/sha}","trees_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/git/trees{/sha}","statuses_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/statuses/{sha}","languages_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/languages","stargazers_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/stargazers","contributors_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/contributors","subscribers_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/subscribers","subscription_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/subscription","commits_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/commits{/sha}","git_commits_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/git/commits{/sha}","comments_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/comments{/number}","issue_comment_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/issues/comments{/number}","contents_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/contents/{+path}","compare_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/compare/{base}...{head}","merges_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/merges","archive_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/downloads","issues_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/issues{/number}","pulls_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/pulls{/number}","milestones_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/milestones{/number}","notifications_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/labels{/name}","releases_url":"https://api.github.com/repos/adaptlearning/adapt_authoring/releases{/id}","created_at":"2013-10-14T13:52:24Z","updated_at":"2015-09-02T07:12:32Z","pushed_at":"2015-09-02T16:06:53Z","git_url":"git://github.com/adaptlearning/adapt_authoring.git","ssh_url":"git@github.com:adaptlearning/adapt_authoring.git","clone_url":"https://github.com/adaptlearning/adapt_authoring.git","svn_url":"https://github.com/adaptlearning/adapt_authoring","homepage":null,"size":43498,"stargazers_count":45,"watchers_count":45,"language":"JavaScript","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":38,"mirror_url":null,"open_issues_count":104,"forks":38,"open_issues":104,"watchers":45,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/adaptlearning/adapt_authoring/pulls/801"},"html":{"href":"https://github.com/adaptlearning/adapt_authoring/pull/801"},"issue":{"href":"https://api.github.com/repos/adaptlearning/adapt_authoring/issues/801"},"comments":{"href":"https://api.github.com/repos/adaptlearning/adapt_authoring/issues/801/comments"},"review_comments":{"href":"https://api.github.com/repos/adaptlearning/adapt_authoring/pulls/801/comments"},"review_comment":{"href":"https://api.github.com/repos/adaptlearning/adapt_authoring/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/adaptlearning/adapt_authoring/pulls/801/commits"},"statuses":{"href":"https://api.github.com/repos/adaptlearning/adapt_authoring/statuses/17c8ad27778921b5523d581912f2d307af6d14ec"}}}}