{"action":"created","comment":{"url":"https://api.github.com/repos/vanilla/addons/pulls/comments/34261537","id":34261537,"diff_hunk":"@@ -0,0 +1,782 @@\n+<?php\n+\n+class GeoipImport {\n+\n+    /**\n+     * Download URL for ZIP file containing CSV data.\n+     *\n+     * @var string\n+     */\n+    private static $csvDownloadURL = \"http://geolite.maxmind.com/download/geoip/database/GeoLite2-City-CSV.zip\";\n+\n+    /**\n+     * Name of CSV block file.\n+     *\n+     * @var string\n+     */\n+    private static $blockFileName    = 'GeoLite2-City-Blocks-IPv4.csv';\n+\n+    /**\n+     * Name of CSV location file.\n+     *\n+     * @var string\n+     */\n+    private static $locationFileName = 'GeoLite2-City-Locations-%s.csv';\n+\n+    /**\n+     * List of possible locales for locations.\n+     *\n+     * @var array\n+     */\n+    private static $locationLocales  = ['de','en','es','fr','ja','pt-BR','ru','zh-CN'];\n+\n+    /**\n+     * Truncate table SQL command.\n+     */\n+    const tuncateQuery  = \"TRUNCATE `%s`\";\n+\n+    /**\n+     * Describe table SQL command.\n+     */\n+    const describeQuery = \"DESCRIBE `%s`\";\n+\n+    /**\n+     * Drop table SQL command.\n+     */\n+    const dropTableQuery = \"DROP TABLE %s IF EXISTS\";\n+\n+    /**\n+     * Number of rows from block CSV to import per Query\n+     *\n+     * @var int\n+     */\n+    private static $blockRowsPerQuery    = 2000;\n+\n+    /**\n+     * Number of rows from location CSV to import per Query\n+     *\n+     * @var int\n+     */\n+    private static $locationRowsPerQuery = 500;\n+\n+    /**\n+     * Time taken to import block CSV file.\n+     *\n+     * @var int\n+     */\n+    private $blockImportTime      = 0;\n+\n+    /**\n+     * Time taken to import location CSV file.\n+     *\n+     * @var int\n+     */\n+    private $locationImportTime   = 0;\n+\n+    /**\n+     * Name of IP block table.\n+     *\n+     * @var string\n+     */\n+    private static $blockTableName       = 'geoip_block';\n+\n+    /**\n+     * Name of IP location table.\n+     *\n+     * @var string\n+     */\n+    private static $locationTableName    = 'geoip_location';\n+\n+    /**\n+     * Path to temp downloaded ZIP file.\n+     *\n+     * @var\n+     */\n+    public $downloadZip;\n+\n+    /**\n+     * Path to temp local location file extracted from zip.\n+     *\n+     * @var\n+     */\n+    public $locationFile;\n+\n+    /**\n+     * Path to temp local block file extracted from zip.\n+     *\n+     * @var\n+     */\n+    public $blockFile;\n+\n+    /**\n+     * Base working directory for downloading and extracting CSV data.\n+     *\n+     * @var string\n+     */\n+    public  $baseDir = '/tmp/geoip';\n+\n+    /**\n+     * User specific directory used for extracting CSV data.\n+     *\n+     * @var string\n+     */\n+    private $userDir;\n+\n+    /**\n+     * Path to temporary error log generated by import routine.\n+     *\n+     * @var string\n+     */\n+    private static $errorLog = \"/tmp/geoip.log\";\n+\n+\n+    function __construct() {\n+\n+        // Set user's import directory:\n+        $this->userDir = $this->baseDir.'_'.GDN::session()->UserID;\n+\n+        return;\n+    }\n+\n+\n+    /**\n+     * Runs primary import procedure for importing GeoIP CSV files into MySQL.\n+     *\n+     * @return bool Returns TRUE on success, FALSE on failure.\n+     */\n+    public function run() {\n+\n+        ini_set('max_execution_time', 600);\n+\n+        $oldErrorOn  = ini_set(\"log_errors\", true);\n+        $oldErrorLog = ini_set(\"error_log\", self::$errorLog);\n+\n+        error_log(\">> ...Starting GeoIP CSV Import... <<\");\n+        error_log(\"Log File: \".self::$errorLog);\n+\n+        // Clean previous working files:\n+        $this->trash();\n+\n+        // Download Files:\n+        $downloaded = $this->importDownload();\n+        if ($downloaded == false) {\n+            error_log(\"Failed to download and extract GeoIP files!\");\n+            return false;\n+        }\n+\n+        // Import Data:\n+        $imported = $this->importData();\n+        if ($imported == false) {\n+            error_log(\"Failed to Import data into MySQL!\");\n+            return false;\n+        }\n+\n+        // Clean up after ourselves:\n+        $this->trash();\n+\n+        error_log(\"|| ...OK: Done importing GeoIP... ||\");\n+\n+        // Reset INI:\n+        ini_set(\"log_errors\", $oldErrorOn);\n+        ini_set(\"error_log\", $oldErrorLog);\n+\n+        return true;\n+    }\n+\n+\n+    /**\n+     * Downloads and extracts GeoIP-Lite City CSV database from Maxmind and\n+     * returns array with temporary paths of extracted files.\n+     *\n+     * @return array Returns array with location and block CSV on success, false on failure.\n+     */\n+    private function importDownload() {\n+\n+        // Download Zip:\n+        $this->downloadZip = $this->downloadGeoipZip(self::$csvDownloadURL);\n+        if (!is_file($this->downloadZip)) {\n+            error_log(\"Failed to download GeoIP CSV file in \".__METHOD__.\"()\");\n+            return false;\n+        }\n+        error_log(\"Zip Downloaded: {$this->downloadZip}\");\n+\n+        // Extract downloaded payload file to get to the CSV files:\n+        $payloadFiles = $this->extractGeoipCSV($this->downloadZip);\n+        if (empty($payloadFiles) || !is_array($payloadFiles)) {\n+            error_log(\"Failed to extract GeoIP CSV file in \".__METHOD__.\"()\");\n+            return false;\n+        }\n+\n+        // SET Files to local properties:\n+        $this->locationFile = !empty($payloadFiles['location_file']) ? $payloadFiles['location_file'] : false;\n+        $this->blockFile    = !empty($payloadFiles['block_file'])    ? $payloadFiles['block_file']    : false;\n+        //error_log(\"Zip Extracted\");\n+\n+        // Check File Paths:\n+        if (!is_file($this->locationFile)) {\n+            error_log(\"Failed to locate GeoIP CSV location file in \".__METHOD__.\"()\");\n+            return false;\n+        }\n+        if (!is_file($this->blockFile)) {\n+            error_log(\"Failed to locate GeoIP CSV block file in \".__METHOD__.\"()\");\n+            return false;\n+        }\n+        //error_log(\"Zip Content Confirmed ({$this->blockFile}, {$this->locationFile})\");\n+        error_log(\"Zip Content Confirmed.\");\n+\n+        return array($this->locationFile, $this->blockFile);\n+    }\n+\n+    /**\n+     * Imports location and block CSV files and loads them into database.\n+     *\n+     * @return bool Returns true on success, false on failure.\n+     */\n+    private function importData() {\n+\n+        if (!is_file($this->locationFile)) {\n+            error_log(\"Failed to locate GeoIP CSV location file in \".__METHOD__.\"()\");\n+            return false;\n+        }\n+        if (!is_file($this->blockFile)) {\n+            error_log(\"Failed to locate GeoIP CSV block file in \".__METHOD__.\"()\");\n+            return false;\n+        }\n+\n+        // Create Location Table:\n+        $locationCreated = $this->createLocationTable();\n+        if (empty($locationCreated)) {\n+            error_log(\"Failed to create GeoIP location table in \".__METHOD__.\"()\");\n+            return false;\n+        }\n+        error_log(\"Location Table Created\");\n+\n+        // Create Block Table:\n+        $blockCreated = $this->createBlockTable();\n+        if (empty($blockCreated)) {\n+            error_log(\"Failed to create GeoIP block table in \".__METHOD__.\"()\");\n+            return false;\n+        }\n+        error_log(\"Block Table Created\");\n+\n+\n+        // Import Location CSV file into SQL:\n+        $locStart = microtime(true);\n+        $locationImported = $this->importLocationCSV($this->locationFile);\n+        if (empty($locationImported)) {\n+            error_log(\"Failed to import GeoIP CSV location file into SQL table in \".__METHOD__.\"()\");\n+            return false;\n+        }\n+        $this->locationImportTime = microtime(true) - $locStart;\n+        error_log(\"Location Table Imported in {$this->locationImportTime} seconds.\");\n+\n+\n+        // Import Block CSV file into SQL:\n+        $blockStart = microtime(true);\n+        $blockImported = $this->importBlockCSV($this->blockFile);\n+        if (empty($blockImported)) {\n+            error_log(\"Failed to import GeoIP CSV block file into SQL table in \".__METHOD__.\"()\");\n+            return false;\n+        }\n+        $this->blockImportTime = microtime(true) - $blockStart;\n+        error_log(\"Block Table Imported in {$this->blockImportTime} seconds.\");\n+\n+        return true;\n+    }\n+\n+\n+    /**\n+     * Downloads target GeoIP zip payload file that contains CSV files\n+     * that will be loaded into DB.\n+     *\n+     * @param null $url Optional URL parameter of target payload.\n+     * @return string\n+     */\n+    private function downloadGeoipZip($url = null) {\n+\n+        // Remove time limit for php execution:\n+        set_time_limit(0);\n+\n+        $url  = !empty($url) ? $url : self::$csvDownloadURL;\n+        $name = substr($url, strrpos($url,'/')+1);\n+\n+        // Create TMP User Dir:\n+        //$this->userDir = \"{$this->baseDir}_\".Gdn::session()->UserID;\n+        if (!is_dir($this->userDir)) {\n+            mkdir($this->userDir, 0775, true);\n+        }\n+\n+        // Output destination file:\n+        $output = \"{$this->userDir}/{$name}\";\n+\n+        $fp = fopen ($output, 'w+');//This is the file where we save the    information\n+        $ch = curl_init($url);//Here is the file we are downloading, replace spaces with %20\n+        //curl_setopt($ch, CURLOPT_TIMEOUT, 50);\n+        curl_setopt($ch, CURLOPT_FILE, $fp); // write curl response to file\n+        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);\n+        curl_exec($ch); // get curl response\n+        curl_close($ch);\n+\n+        return $output;\n+    }\n+\n+    /**\n+     * Extracts downloaded GeoIP-Lite City CSV file and returns path to location and block files.\n+     *\n+     * @param $input string Path to target ZIP file.\n+     * @param string $locale Optional locale to be used.\n+     * @return array|bool Returns array of extracted files on success, false on failure.\n+     */\n+    private function extractGeoipCSV($input, $locale = 'en') {\n+        if (!is_file($input)) {\n+            error_log(\"Invalid Zip file passed for extraction in \".__METHOD__.\"()\");\n+            return false;\n+        }\n+\n+        $baseDir = dirname($input);\n+\n+        // UnZip Archive:\n+        $zip = new ZipArchive;\n+\n+        if ($zip->open($input) === true) {\n+            $zip->extractTo($baseDir);\n+            $zip->close();\n+        } else {\n+            return false;\n+        }\n+\n+        // Get Extracted Directory Name:\n+        $geoipDir = self::locateSubDir($baseDir);\n+        error_log(\"GeoIP Directory: {$baseDir}/{$geoipDir}\");\n+\n+        $output = [];\n+        $output['block_file']    = \"{$baseDir}/{$geoipDir}/\".self::$blockFileName;\n+        $output['location_file'] = \"{$baseDir}/{$geoipDir}/\".sprintf(self::$locationFileName, $locale);\n+\n+        // Check extracted payload files are properly linked:\n+        if (!is_file($output['block_file'])) {\n+            error_log(\"Could not locate extract GeoIP Block file in \".__METHOD__.\"()!\");\n+        }\n+        if (!is_file($output['location_file'])) {\n+            error_log(\"Could not locate extract GeoIP Location file in \".__METHOD__.\"()!\");\n+        }\n+\n+        // Delete original Zip file.\n+        $deleted = unlink($input);\n+        if ($deleted == false) {\n+            error_log(\"ERROR: Failed to delete original downloaded ZIP file.\");\n+        }\n+\n+        return $output;\n+    }\n+\n+    /**\n+     * Creates MySQL geoip location table.\n+     *\n+     * @return bool|Gdn_DataSet|object|string\n+     */\n+    private function createLocationTable() {\n+\n+        error_log(\"Creating Location Table\");\n+        if ($this->tableExists(self::$locationTableName) == false) {\n+\n+            try {\n+                $sql = \"CREATE TABLE \".self::$locationTableName.\" (\\n\";\n+                $sql .= \"  `geoname_id` int(10) unsigned NOT NULL\\n\";\n+                $sql .= \", `locale_code` char(2) NOT NULL\\n\";\n+                $sql .= \", `continent_code` char(2) NOT NULL\\n\";\n+                $sql .= \", `continent_name` varchar(24) NOT NULL\\n\";\n+                $sql .= \", `country_iso_code` char(2) NOT NULL\\n\";\n+                $sql .= \", `country_name` varchar(36) NOT NULL\\n\";\n+                $sql .= \", `subdivision_1_iso_code` char(3) NOT NULL\\n\";\n+                $sql .= \", `subdivision_1_name` varchar(36) NOT NULL\\n\";\n+                $sql .= \", `subdivision_2_iso_code` char(3) NOT NULL\\n\";\n+                $sql .= \", `subdivision_2_name` varchar(36) NOT NULL\\n\";\n+                $sql .= \", `city_name` varchar(50) NOT NULL\\n\";\n+                $sql .= \", `metro_code` int(4) NOT NULL\\n\";\n+                $sql .= \", `time_zone` varchar(24) NOT NULL\\n\";\n+                $sql .= \", PRIMARY KEY (`geoname_id`)\\n\";\n+                $sql .= \") ENGINE=InnoDB DEFAULT CHARSET=utf8\\n;\";\n+                //error_log(\"Create Location Table:\\n{$sql}\");\n+\n+                $output = GDN::SQL()->Query($sql);\n+\n+            } catch(\\Exception $e) {\n+                error_log(\"SQL Error: \".$e->getMessage());\n+                return false;\n+            }\n+\n+        } else {\n+            error_log(\"-Location table exists.\");\n+            $this->tableTruncate(self::$locationTableName);\n+            return true;\n+        }\n+\n+        return $output;\n+    }\n+\n+    /**\n+     * Imports GeoIP location CSV file into MySQL database.\n+     *\n+     * @param $input Filename of CSV to import.\n+     * @return bool\n+     */\n+    private function importLocationCSV($input) {\n+        if (empty($input) || !is_file($input)) {\n+            trigger_error(\"Invalid path to block CSV in \".__METHOD__.\"()!\", E_USER_WARNING);\n+            return false;\n+        }\n+\n+        $fh  = fopen($input,'r');\n+        $rem = null;\n+        $i   = 0;\n+\n+        while (!feof($fh)) {\n+\n+            $sql  = \"INSERT into \".self::$locationTableName.\"\\n\";\n+            $sql .= \"(geoname_id, locale_code, continent_code, continent_name, country_iso_code, country_name, subdivision_1_iso_code, subdivision_1_name, subdivision_2_iso_code, subdivision_2_name, city_name, metro_code, time_zone)\\n\";\n+            $sql .= \"VALUES\\n\";\n+\n+            $j = 0;\n+            while ($j < self::$locationRowsPerQuery && !feof($fh)) {\n+\n+                $cells = fgetcsv($fh, 1024, \",\", '\"');\n+                if (empty($cells[0])) {\n+                    continue;\n+                }\n+\n+                if (!is_numeric($cells[0])) {\n+                    continue;\n+                }\n+\n+                $sql .= (($j == 0) ? \"  \" : \", \");\n+                // $sql .= \"('{$cells[0]}','{$cells[1]}','{$cells[2]}','\".str_replace(\"'\",\"\\\\'\",$cells[3]).\"','\".str_replace(\"'\",\"\\\\'\",$cells[4]).\"','\".str_replace(\"'\",\"\\\\'\",$cells[5]).\"','\".str_replace(\"'\",\"\\\\'\",$cells[6]).\"','\".str_replace(\"'\",\"\\\\'\",$cells[7]).\"','\".str_replace(\"'\",\"\\\\'\",$cells[8]).\"','\".str_replace(\"'\",\"\\\\'\",$cells[9]).\"','\".str_replace(\"'\",\"\\\\'\",$cells[10]).\"','\".str_replace(\"'\",\"\\\\'\",$cells[11]).\"','\".str_replace(\"'\",\"\\\\'\",$cells[12]).\"')\\n\";\n+                $sql .= \"('\".str_replace(\"'\",\"\\\\'\",$cells[0]).\"'\"\n+                    . \",'\".str_replace(\"'\",\"\\\\'\",$cells[1]).\"'\"\n+                    . \",'\".str_replace(\"'\",\"\\\\'\",$cells[2]).\"'\"\n+                    . \",'\".str_replace(\"'\",\"\\\\'\",$cells[3]).\"'\"\n+                    . \",'\".str_replace(\"'\",\"\\\\'\",$cells[4]).\"'\"\n+                    . \",'\".str_replace(\"'\",\"\\\\'\",$cells[5]).\"'\"\n+                    . \",'\".str_replace(\"'\",\"\\\\'\",$cells[6]).\"'\"\n+                    . \",'\".str_replace(\"'\",\"\\\\'\",$cells[7]).\"'\"\n+                    . \",'\".str_replace(\"'\",\"\\\\'\",$cells[8]).\"'\"\n+                    . \",'\".str_replace(\"'\",\"\\\\'\",$cells[9]).\"'\"\n+                    . \",'\".str_replace(\"'\",\"\\\\'\",$cells[10]).\"'\"\n+                    . \",'\".str_replace(\"'\",\"\\\\'\",$cells[11]).\"'\"\n+                    . \",'\".str_replace(\"'\",\"\\\\'\",$cells[12]).\"'\"\n+                    . \")\\n\";\n+                $j++;\n+            }\n+            $sql .= \";\";\n+            // error_log(\"-{$i})\\n{$sql}\");\n+\n+            try {\n+                $PDO = GDN::database()->connection();\n+                $PDO->query($sql);\n+            } catch(Exception $e) {","path":"plugins/GeoIP/class.geoip.import.php","position":476,"original_position":476,"commit_id":"cc92771d647735063c81c4263a0b78dddd31f425","original_commit_id":"cc92771d647735063c81c4263a0b78dddd31f425","user":{"login":"kaecyra","id":248212,"avatar_url":"https://avatars.githubusercontent.com/u/248212?v=3","gravatar_id":"","url":"https://api.github.com/users/kaecyra","html_url":"https://github.com/kaecyra","followers_url":"https://api.github.com/users/kaecyra/followers","following_url":"https://api.github.com/users/kaecyra/following{/other_user}","gists_url":"https://api.github.com/users/kaecyra/gists{/gist_id}","starred_url":"https://api.github.com/users/kaecyra/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/kaecyra/subscriptions","organizations_url":"https://api.github.com/users/kaecyra/orgs","repos_url":"https://api.github.com/users/kaecyra/repos","events_url":"https://api.github.com/users/kaecyra/events{/privacy}","received_events_url":"https://api.github.com/users/kaecyra/received_events","type":"User","site_admin":false},"body":"Space after catch","created_at":"2015-07-09T14:36:25Z","updated_at":"2015-07-09T14:36:25Z","html_url":"https://github.com/vanilla/addons/pull/231#discussion_r34261537","pull_request_url":"https://api.github.com/repos/vanilla/addons/pulls/231","_links":{"self":{"href":"https://api.github.com/repos/vanilla/addons/pulls/comments/34261537"},"html":{"href":"https://github.com/vanilla/addons/pull/231#discussion_r34261537"},"pull_request":{"href":"https://api.github.com/repos/vanilla/addons/pulls/231"}}},"pull_request":{"url":"https://api.github.com/repos/vanilla/addons/pulls/231","id":39511274,"html_url":"https://github.com/vanilla/addons/pull/231","diff_url":"https://github.com/vanilla/addons/pull/231.diff","patch_url":"https://github.com/vanilla/addons/pull/231.patch","issue_url":"https://api.github.com/repos/vanilla/addons/issues/231","number":231,"state":"open","locked":false,"title":"Feature/geoip","user":{"login":"dericdd","id":223064,"avatar_url":"https://avatars.githubusercontent.com/u/223064?v=3","gravatar_id":"","url":"https://api.github.com/users/dericdd","html_url":"https://github.com/dericdd","followers_url":"https://api.github.com/users/dericdd/followers","following_url":"https://api.github.com/users/dericdd/following{/other_user}","gists_url":"https://api.github.com/users/dericdd/gists{/gist_id}","starred_url":"https://api.github.com/users/dericdd/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dericdd/subscriptions","organizations_url":"https://api.github.com/users/dericdd/orgs","repos_url":"https://api.github.com/users/dericdd/repos","events_url":"https://api.github.com/users/dericdd/events{/privacy}","received_events_url":"https://api.github.com/users/dericdd/received_events","type":"User","site_admin":false},"body":"","created_at":"2015-07-08T19:02:09Z","updated_at":"2015-07-09T14:36:25Z","closed_at":null,"merged_at":null,"merge_commit_sha":"2b2c0000b44dd388aaa1cf9e0332192a6c518542","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/vanilla/addons/pulls/231/commits","review_comments_url":"https://api.github.com/repos/vanilla/addons/pulls/231/comments","review_comment_url":"https://api.github.com/repos/vanilla/addons/pulls/comments{/number}","comments_url":"https://api.github.com/repos/vanilla/addons/issues/231/comments","statuses_url":"https://api.github.com/repos/vanilla/addons/statuses/cc92771d647735063c81c4263a0b78dddd31f425","head":{"label":"vanilla:feature/geoip","ref":"feature/geoip","sha":"cc92771d647735063c81c4263a0b78dddd31f425","user":{"login":"vanilla","id":7443552,"avatar_url":"https://avatars.githubusercontent.com/u/7443552?v=3","gravatar_id":"","url":"https://api.github.com/users/vanilla","html_url":"https://github.com/vanilla","followers_url":"https://api.github.com/users/vanilla/followers","following_url":"https://api.github.com/users/vanilla/following{/other_user}","gists_url":"https://api.github.com/users/vanilla/gists{/gist_id}","starred_url":"https://api.github.com/users/vanilla/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/vanilla/subscriptions","organizations_url":"https://api.github.com/users/vanilla/orgs","repos_url":"https://api.github.com/users/vanilla/repos","events_url":"https://api.github.com/users/vanilla/events{/privacy}","received_events_url":"https://api.github.com/users/vanilla/received_events","type":"Organization","site_admin":false},"repo":{"id":633372,"name":"addons","full_name":"vanilla/addons","owner":{"login":"vanilla","id":7443552,"avatar_url":"https://avatars.githubusercontent.com/u/7443552?v=3","gravatar_id":"","url":"https://api.github.com/users/vanilla","html_url":"https://github.com/vanilla","followers_url":"https://api.github.com/users/vanilla/followers","following_url":"https://api.github.com/users/vanilla/following{/other_user}","gists_url":"https://api.github.com/users/vanilla/gists{/gist_id}","starred_url":"https://api.github.com/users/vanilla/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/vanilla/subscriptions","organizations_url":"https://api.github.com/users/vanilla/orgs","repos_url":"https://api.github.com/users/vanilla/repos","events_url":"https://api.github.com/users/vanilla/events{/privacy}","received_events_url":"https://api.github.com/users/vanilla/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/vanilla/addons","description":"Official addons for Vanilla Forums","fork":false,"url":"https://api.github.com/repos/vanilla/addons","forks_url":"https://api.github.com/repos/vanilla/addons/forks","keys_url":"https://api.github.com/repos/vanilla/addons/keys{/key_id}","collaborators_url":"https://api.github.com/repos/vanilla/addons/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/vanilla/addons/teams","hooks_url":"https://api.github.com/repos/vanilla/addons/hooks","issue_events_url":"https://api.github.com/repos/vanilla/addons/issues/events{/number}","events_url":"https://api.github.com/repos/vanilla/addons/events","assignees_url":"https://api.github.com/repos/vanilla/addons/assignees{/user}","branches_url":"https://api.github.com/repos/vanilla/addons/branches{/branch}","tags_url":"https://api.github.com/repos/vanilla/addons/tags","blobs_url":"https://api.github.com/repos/vanilla/addons/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/vanilla/addons/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/vanilla/addons/git/refs{/sha}","trees_url":"https://api.github.com/repos/vanilla/addons/git/trees{/sha}","statuses_url":"https://api.github.com/repos/vanilla/addons/statuses/{sha}","languages_url":"https://api.github.com/repos/vanilla/addons/languages","stargazers_url":"https://api.github.com/repos/vanilla/addons/stargazers","contributors_url":"https://api.github.com/repos/vanilla/addons/contributors","subscribers_url":"https://api.github.com/repos/vanilla/addons/subscribers","subscription_url":"https://api.github.com/repos/vanilla/addons/subscription","commits_url":"https://api.github.com/repos/vanilla/addons/commits{/sha}","git_commits_url":"https://api.github.com/repos/vanilla/addons/git/commits{/sha}","comments_url":"https://api.github.com/repos/vanilla/addons/comments{/number}","issue_comment_url":"https://api.github.com/repos/vanilla/addons/issues/comments{/number}","contents_url":"https://api.github.com/repos/vanilla/addons/contents/{+path}","compare_url":"https://api.github.com/repos/vanilla/addons/compare/{base}...{head}","merges_url":"https://api.github.com/repos/vanilla/addons/merges","archive_url":"https://api.github.com/repos/vanilla/addons/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/vanilla/addons/downloads","issues_url":"https://api.github.com/repos/vanilla/addons/issues{/number}","pulls_url":"https://api.github.com/repos/vanilla/addons/pulls{/number}","milestones_url":"https://api.github.com/repos/vanilla/addons/milestones{/number}","notifications_url":"https://api.github.com/repos/vanilla/addons/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/vanilla/addons/labels{/name}","releases_url":"https://api.github.com/repos/vanilla/addons/releases{/id}","created_at":"2010-04-28T14:04:03Z","updated_at":"2015-07-08T08:13:02Z","pushed_at":"2015-07-09T14:29:11Z","git_url":"git://github.com/vanilla/addons.git","ssh_url":"git@github.com:vanilla/addons.git","clone_url":"https://github.com/vanilla/addons.git","svn_url":"https://github.com/vanilla/addons","homepage":"http://vanillaforums.org","size":12312,"stargazers_count":90,"watchers_count":90,"language":"PHP","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":163,"mirror_url":null,"open_issues_count":37,"forks":163,"open_issues":37,"watchers":90,"default_branch":"master"}},"base":{"label":"vanilla:master","ref":"master","sha":"7cd648026a166632b9680a732554c25157de487c","user":{"login":"vanilla","id":7443552,"avatar_url":"https://avatars.githubusercontent.com/u/7443552?v=3","gravatar_id":"","url":"https://api.github.com/users/vanilla","html_url":"https://github.com/vanilla","followers_url":"https://api.github.com/users/vanilla/followers","following_url":"https://api.github.com/users/vanilla/following{/other_user}","gists_url":"https://api.github.com/users/vanilla/gists{/gist_id}","starred_url":"https://api.github.com/users/vanilla/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/vanilla/subscriptions","organizations_url":"https://api.github.com/users/vanilla/orgs","repos_url":"https://api.github.com/users/vanilla/repos","events_url":"https://api.github.com/users/vanilla/events{/privacy}","received_events_url":"https://api.github.com/users/vanilla/received_events","type":"Organization","site_admin":false},"repo":{"id":633372,"name":"addons","full_name":"vanilla/addons","owner":{"login":"vanilla","id":7443552,"avatar_url":"https://avatars.githubusercontent.com/u/7443552?v=3","gravatar_id":"","url":"https://api.github.com/users/vanilla","html_url":"https://github.com/vanilla","followers_url":"https://api.github.com/users/vanilla/followers","following_url":"https://api.github.com/users/vanilla/following{/other_user}","gists_url":"https://api.github.com/users/vanilla/gists{/gist_id}","starred_url":"https://api.github.com/users/vanilla/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/vanilla/subscriptions","organizations_url":"https://api.github.com/users/vanilla/orgs","repos_url":"https://api.github.com/users/vanilla/repos","events_url":"https://api.github.com/users/vanilla/events{/privacy}","received_events_url":"https://api.github.com/users/vanilla/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/vanilla/addons","description":"Official addons for Vanilla Forums","fork":false,"url":"https://api.github.com/repos/vanilla/addons","forks_url":"https://api.github.com/repos/vanilla/addons/forks","keys_url":"https://api.github.com/repos/vanilla/addons/keys{/key_id}","collaborators_url":"https://api.github.com/repos/vanilla/addons/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/vanilla/addons/teams","hooks_url":"https://api.github.com/repos/vanilla/addons/hooks","issue_events_url":"https://api.github.com/repos/vanilla/addons/issues/events{/number}","events_url":"https://api.github.com/repos/vanilla/addons/events","assignees_url":"https://api.github.com/repos/vanilla/addons/assignees{/user}","branches_url":"https://api.github.com/repos/vanilla/addons/branches{/branch}","tags_url":"https://api.github.com/repos/vanilla/addons/tags","blobs_url":"https://api.github.com/repos/vanilla/addons/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/vanilla/addons/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/vanilla/addons/git/refs{/sha}","trees_url":"https://api.github.com/repos/vanilla/addons/git/trees{/sha}","statuses_url":"https://api.github.com/repos/vanilla/addons/statuses/{sha}","languages_url":"https://api.github.com/repos/vanilla/addons/languages","stargazers_url":"https://api.github.com/repos/vanilla/addons/stargazers","contributors_url":"https://api.github.com/repos/vanilla/addons/contributors","subscribers_url":"https://api.github.com/repos/vanilla/addons/subscribers","subscription_url":"https://api.github.com/repos/vanilla/addons/subscription","commits_url":"https://api.github.com/repos/vanilla/addons/commits{/sha}","git_commits_url":"https://api.github.com/repos/vanilla/addons/git/commits{/sha}","comments_url":"https://api.github.com/repos/vanilla/addons/comments{/number}","issue_comment_url":"https://api.github.com/repos/vanilla/addons/issues/comments{/number}","contents_url":"https://api.github.com/repos/vanilla/addons/contents/{+path}","compare_url":"https://api.github.com/repos/vanilla/addons/compare/{base}...{head}","merges_url":"https://api.github.com/repos/vanilla/addons/merges","archive_url":"https://api.github.com/repos/vanilla/addons/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/vanilla/addons/downloads","issues_url":"https://api.github.com/repos/vanilla/addons/issues{/number}","pulls_url":"https://api.github.com/repos/vanilla/addons/pulls{/number}","milestones_url":"https://api.github.com/repos/vanilla/addons/milestones{/number}","notifications_url":"https://api.github.com/repos/vanilla/addons/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/vanilla/addons/labels{/name}","releases_url":"https://api.github.com/repos/vanilla/addons/releases{/id}","created_at":"2010-04-28T14:04:03Z","updated_at":"2015-07-08T08:13:02Z","pushed_at":"2015-07-09T14:29:11Z","git_url":"git://github.com/vanilla/addons.git","ssh_url":"git@github.com:vanilla/addons.git","clone_url":"https://github.com/vanilla/addons.git","svn_url":"https://github.com/vanilla/addons","homepage":"http://vanillaforums.org","size":12312,"stargazers_count":90,"watchers_count":90,"language":"PHP","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":163,"mirror_url":null,"open_issues_count":37,"forks":163,"open_issues":37,"watchers":90,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/vanilla/addons/pulls/231"},"html":{"href":"https://github.com/vanilla/addons/pull/231"},"issue":{"href":"https://api.github.com/repos/vanilla/addons/issues/231"},"comments":{"href":"https://api.github.com/repos/vanilla/addons/issues/231/comments"},"review_comments":{"href":"https://api.github.com/repos/vanilla/addons/pulls/231/comments"},"review_comment":{"href":"https://api.github.com/repos/vanilla/addons/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/vanilla/addons/pulls/231/commits"},"statuses":{"href":"https://api.github.com/repos/vanilla/addons/statuses/cc92771d647735063c81c4263a0b78dddd31f425"}}}}