├── system └── expressionengine │ └── third_party │ └── github_addon_installer │ ├── src │ ├── Installer │ │ ├── SystemUnzipInstaller.php │ │ ├── ZipArchiveInstaller.php │ │ ├── FileInstaller.php │ │ ├── AbstractZipInstaller.php │ │ └── Installer.php │ ├── Application.php │ ├── Repo.php │ └── Api.php │ ├── language │ └── english │ │ └── github_addon_installer_lang.php │ ├── views │ └── index.php │ ├── upd.github_addon_installer.php │ ├── mcp.github_addon_installer.php │ ├── libraries │ └── Github_addon_installer.php │ └── config │ └── manifest.js ├── composer.json └── README.markdown /system/expressionengine/third_party/github_addon_installer/src/Installer/SystemUnzipInstaller.php: -------------------------------------------------------------------------------- 1 | tempPath, $filePath)); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "eecli/github-addon-installer", 3 | "description": "Install Github-hosted Addons with eecli.", 4 | "authors": [ 5 | { 6 | "name": "Rob Sanchez", 7 | "email": "info@robsanchez.com" 8 | } 9 | ], 10 | "autoload": { 11 | "psr-4": { "eecli\\GithubAddonInstaller\\": "system/expressionengine/third_party/github_addon_installer/src/" } 12 | }, 13 | "require": {} 14 | } 15 | -------------------------------------------------------------------------------- /system/expressionengine/third_party/github_addon_installer/src/Installer/ZipArchiveInstaller.php: -------------------------------------------------------------------------------- 1 | open($filePath); 14 | 15 | $zip->extractTo($this->tempPath); 16 | 17 | $zip->close(); 18 | 19 | unset($zip); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /system/expressionengine/third_party/github_addon_installer/src/Installer/FileInstaller.php: -------------------------------------------------------------------------------- 1 | getBlob($file->sha); 12 | 13 | if (isset($data->content)) { 14 | $handle = fopen($destination, 'w'); 15 | 16 | fwrite($handle, base64_decode($data->content)); 17 | 18 | fclose($handle); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /system/expressionengine/third_party/github_addon_installer/language/english/github_addon_installer_lang.php: -------------------------------------------------------------------------------- 1 | 5 | 'GitHub Addon Installer', 6 | 7 | 'github_addon_installer_module_description' => 8 | 'Install and update addons with public git repositories.', 9 | 10 | // Start inserting custom language keys/values here 11 | 12 | 'incomplete_repo_definition' => 'The definition for this repo is incomplete.', 13 | 'branch_not_found' => 'Branch %s not found', 14 | 'repo_not_found' => 'Repo %s not found', 15 | 'fetch_dir_doesnt_exist' => 'The directory %s does not exist.', 16 | 'path_third_not_writable' => 'The third_party/ cannot be written to.', 17 | 'temp_dir_not_writable' => 'The temp directory cannot be written to.', 18 | 'cant_write_file' => 'The installer could not write the file %s.', 19 | 'successfully_installed' => 'Successfully installed %s.', 20 | 'invalid_addon' => 'No definition found for %s.', 21 | 'addon_name' => 'Addon', 22 | 'addon_status' => 'Status', 23 | 'addon_install' => 'Install', 24 | 'addon_not_installed' => 'Not Installed', 25 | 'addon_installed' => 'Installed', 26 | 'addon_installing' => 'Installing...', 27 | 'addon_reinstall' => 'Re-install/Update', 28 | 'addon_update' => 'Update Available',// %s', 29 | 'addon_author' => 'Author', 30 | 'addon_branch' => 'Branch', 31 | 'addon_stars' => 'Stars', 32 | 'addon_github_url' => 'GitHub URL', 33 | 'filter_by_status' => 'Filter By Status', 34 | 'filter_by_author' => 'Filter By Author', 35 | 'keyword' => 'Keyword', 36 | 'filter_by_keyword' => 'Filter By Keyword', 37 | 'filter' => 'Filter', 38 | 'no_filter' => 'No Filter', 39 | ); 40 | 41 | /* End of file lang.github_addon_installer.php */ 42 | /* Location: /system/expressionengine/third_party/github_addon_installer/language/english/lang.github_addon_installer.php */ 43 | -------------------------------------------------------------------------------- /system/expressionengine/third_party/github_addon_installer/views/index.php: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |
5 |
6 | 14 |
15 | 16 |
17 | 18 | 19 |
20 | 21 |
22 |
23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 |
 
-------------------------------------------------------------------------------- /system/expressionengine/third_party/github_addon_installer/src/Installer/AbstractZipInstaller.php: -------------------------------------------------------------------------------- 1 | tempPath.$repo->getSha().'.zip'; 16 | 17 | if ( ! file_exists($filePath)) { 18 | $handle = fopen($filePath, 'w'); 19 | 20 | fwrite($handle, $repo->getZipball()); 21 | 22 | fclose($handle); 23 | 24 | $this->unzip($filePath); 25 | } 26 | 27 | //this is how github names the zipball, usually 28 | $shortSha = substr($repo->getSha(), 0, 7); 29 | 30 | $tempDir = $repo->getUser().'-'.$repo->getRepo().'-'.$shortSha; 31 | 32 | $files = array(); 33 | 34 | //the sha numbering is off, else is a somewhat messy fallback 35 | if (! is_dir($this->tempPath.$tempDir)) { 36 | $tempDir = null; 37 | 38 | $iterator = new \DirectoryIterator($this->tempPath); 39 | 40 | foreach ($iterator as $file) { 41 | // does it start with this user/repo combo? 42 | if (! $file->isDot() && $file->isDir() && preg_match('/^'.preg_quote($repo->getUser()).'-'.preg_quote($repo->getRepo()).'-/', $file->getBasename())) { 43 | $tempDir = $file->getBasename(); 44 | break; 45 | } 46 | } 47 | } 48 | 49 | if (is_null($tempDir)) { 50 | throw new \Exception(sprintf('Could not find unzipped contents for %s/%s in %s', $repo->getUser(), $repo->getRepo(), $this->tempPath)); 51 | } 52 | 53 | $this->tempDir = $tempDir; 54 | 55 | parent::install($repo, $addonName, $addonPath, $themePath, $addFolder); 56 | 57 | unlink($this->tempPath.$repo->getSha().'.zip'); 58 | } 59 | 60 | protected function copyFile(Repo $repo, $file, $destination) 61 | { 62 | rename($this->tempPath.$this->tempDir.'/'.$file->path, $destination); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /system/expressionengine/third_party/github_addon_installer/upd.github_addon_installer.php: -------------------------------------------------------------------------------- 1 | 'Github_addon_installer', 40 | 'module_version' => $this->version, 41 | 'has_cp_backend' => "y", 42 | 'has_publish_fields' => 'n' 43 | ); 44 | 45 | ee()->db->insert('modules', $mod_data); 46 | 47 | // ee()->load->dbforge(); 48 | /** 49 | * In order to setup your custom tables, uncomment the line above, and 50 | * start adding them below! 51 | */ 52 | 53 | return TRUE; 54 | } 55 | 56 | // ---------------------------------------------------------------- 57 | 58 | /** 59 | * Uninstall 60 | * 61 | * @return boolean TRUE 62 | */ 63 | public function uninstall() 64 | { 65 | $mod_id = ee()->db->select('module_id') 66 | ->get_where('modules', array( 67 | 'module_name' => 'Github_addon_installer' 68 | ))->row('module_id'); 69 | 70 | ee()->db->where('module_id', $mod_id) 71 | ->delete('module_member_groups'); 72 | 73 | ee()->db->where('module_name', 'Github_addon_installer') 74 | ->delete('modules'); 75 | 76 | // ee()->load->dbforge(); 77 | // Delete your custom tables & any ACT rows 78 | // you have in the actions table 79 | 80 | return TRUE; 81 | } 82 | 83 | // ---------------------------------------------------------------- 84 | 85 | /** 86 | * Module Updater 87 | * 88 | * @return boolean TRUE 89 | */ 90 | public function update($current = '') 91 | { 92 | // If you have updates, drop 'em in here. 93 | return TRUE; 94 | } 95 | 96 | } 97 | /* End of file upd.github_addon_installer.php */ 98 | /* Location: /system/expressionengine/third_party/github_addon_installer/upd.github_addon_installer.php */ -------------------------------------------------------------------------------- /system/expressionengine/third_party/github_addon_installer/src/Application.php: -------------------------------------------------------------------------------- 1 | manifest = json_decode($manifestContents, true); 36 | 37 | if (! $this->manifest) { 38 | throw new \Exception('Could not load the Github Addon Installer manifest.'); 39 | } 40 | 41 | ksort($this->manifest); 42 | 43 | $this->api = new Api(); 44 | 45 | $this->installer = Installer::create($thirdPartyPath, $themePath, $tempPath); 46 | } 47 | 48 | public function getApi() 49 | { 50 | return $this->api; 51 | } 52 | 53 | public function getInstaller() 54 | { 55 | return $this->installer; 56 | } 57 | 58 | public function getManifest() 59 | { 60 | return $this->manifest; 61 | } 62 | 63 | public function getRepo($addon, $branch = null) 64 | { 65 | if (! isset($this->manifest[$addon])) { 66 | throw new \Exception('Addon not found in manifest.'); 67 | } 68 | 69 | if (! $branch) { 70 | $branch = isset($this->manifest[$addon]['branch']) ? isset($this->manifest[$addon]['branch']) : 'master'; 71 | } 72 | 73 | return new Repo( 74 | $this->api, 75 | $this->manifest[$addon]['user'], 76 | $this->manifest[$addon]['repo'], 77 | $branch 78 | ); 79 | } 80 | 81 | public function installAddon($addon, $branch = null) 82 | { 83 | return $this->installer->install( 84 | $this->getRepo($addon, $branch), 85 | $addon, 86 | isset($this->manifest[$addon]['addon_path']) ? $this->manifest[$addon]['addon_path'] : null, 87 | isset($this->manifest[$addon]['theme_path']) ? $this->manifest[$addon]['theme_path'] : null, 88 | isset($this->manifest[$addon]['add_folder']) ? $this->manifest[$addon]['add_folder'] : false 89 | ); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /system/expressionengine/third_party/github_addon_installer/src/Installer/Installer.php: -------------------------------------------------------------------------------- 1 | thirdPartyPath = $thirdPartyPath; 18 | 19 | $this->themePath = $themePath; 20 | 21 | $this->tempPath = $tempPath; 22 | } 23 | 24 | public static function create($thirdPartyPath, $themePath, $tempPath) 25 | { 26 | if (extension_loaded('zip')) { 27 | return new ZipArchiveInstaller($thirdPartyPath, $themePath, $tempPath); 28 | } elseif (shell_exec('which unzip')) { 29 | return new SystemUnzipInstaller($thirdPartyPath, $themePath, $tempPath); 30 | } 31 | 32 | return new FileInstaller($thirdPartyPath, $themePath, $tempPath); 33 | } 34 | 35 | abstract protected function copyFile(Repo $repo, $file, $destination); 36 | 37 | public function install(Repo $repo, $addonName, $addonPath = null, $themePath = null, $addFolder = false) 38 | { 39 | foreach ($repo->getFiles() as $file) { 40 | $path = $this->thirdPartyPath; 41 | 42 | $destination = $file->path; 43 | 44 | $proceed = false; 45 | 46 | if ($addonPath) { 47 | if (strncmp($destination, $addonPath, strlen($addonPath)) === 0) { 48 | $proceed = true; 49 | 50 | $destination = str_replace($addonPath, '', $destination); 51 | 52 | if ($addFolder) { 53 | $destination = is_bool($addFolder) ? $addonName.'/'.$destination : $addFolder.'/'.$destination; 54 | } 55 | } 56 | } 57 | 58 | if ($proceed === false && $themePath) { 59 | if (strncmp($destination, $themePath, strlen($themePath)) === 0) { 60 | $proceed = true; 61 | 62 | $destination = str_replace($themePath, '', $destination); 63 | 64 | $path = $this->themePath; 65 | } 66 | } 67 | 68 | if (! $addonPath && $proceed === false && ! $repo->isFileIgnored($destination)) { 69 | $proceed = true; 70 | 71 | if ($addFolder) { 72 | $destination = is_bool($addFolder) ? $addonName.'/'.$destination : $addFolder.'/'.$destination; 73 | } 74 | } 75 | 76 | if ($proceed === false) { 77 | continue; 78 | } 79 | 80 | if (strpos($destination, '/') !== false) { 81 | $path = realpath($path); 82 | 83 | $dirs = explode('/', $destination); 84 | 85 | $path .= '/'; 86 | 87 | $destination = array_pop($dirs); 88 | 89 | foreach ($dirs as $dir) { 90 | if (! is_dir($path.$dir)) { 91 | @mkdir($path.$dir, 0777); 92 | } 93 | 94 | $path .= $dir.'/'; 95 | } 96 | } 97 | 98 | $destination = $path.$destination; 99 | 100 | if (! is_writable($path) || (file_exists($destination) && ! is_writable($destination))) { 101 | throw new \Exception(sprintf('Cannot write file %s', $destination)); 102 | } 103 | 104 | $this->copyFile($repo, $file, $destination); 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | # GitHub Addon Installer for ExpressionEngine 2 | 3 | Install and update free EE addons found on Github. 4 | 5 | ![GitHub Addon Installer Screenshot](http://f.cl.ly/items/0b1z031o2l3g2X221E1Z/Screen%20shot%202011-07-04%20at%2012.09.38%20PM.png) 6 | 7 | ## About 8 | 9 | How it works: it downloads a zip file from Github to your temp dir and then it unzips it to your third_party/ folder. If the addon already exists (aka you are updating it), it will be overwritten. If the addon has an installer you will be redirected to the installer page. That's it, no other magic involved. 10 | 11 | This addon is not meant to be run on a production environment. You are encouraged to use this addon on local and/or staging environments only. Your third_party folder on your production environment should not be writable. 12 | 13 | I always use this locally on a version controlled repository, where I can easily roll back any changes. 14 | 15 | ## Installation 16 | 17 | * Copy the system/expressionengine/third_party/github_addon_installer/ folder to system/expressionengine/third_party/ 18 | * Install the module 19 | * Make sure your system/expressionengine/cache/ and system/expressionengine/third_party/ (or your user-defined third_party) directories are writable 20 | 21 | ## Updating 22 | 23 | This addon does not use traditional version releases, but rather rolling releases, as new addons are added to and removed from the manifest. You are encouraged to use Github Addon Installer to update itself. 24 | 25 | ## Requirements 26 | 27 | * ExpressionEngine 2.6+ 28 | * PHP 5.2+ 29 | * *nix server (no Windows/IIS) 30 | 31 | ## Usage 32 | 33 | Go to Add-Ons > Modules > Github Addon Installer. You are shown an alphabetical list of all the eligible addons. You can filter this list by status (Installed or Not Installed) or by author. You can also type in keywords to quickly find an addon by name using a fuzzy search. 34 | 35 | Click the "Install" button to install an addon. 36 | 37 | ## Config 38 | 39 | If you wish to change your temp dir location, you can add this to your EE config file: 40 | 41 | $config['github_addon_installer_temp_path'] = '/path/to/dir/'; # default is system/expressionengine/cache/github_addon_installer/ 42 | 43 | You can disable Github Addon Installer with a config item. This is useful for disabling in production environments. 44 | 45 | $config['github_addon_installer_disabled'] = $_SERVER['HTTP_HOST'] === 'my-production-site.com'; 46 | 47 | ## Adding an Addon to the Manifest 48 | 49 | The list of eligible addons is stored in github_addon_installer/config/manifest.js. This file contains a JSON object. To add something to this list, fork this project, add repos to the list, and submit a pull request. The key of your manifest entry should be the short name of your add-on. The manifest is indented with two spaces, please adhere to that. Please remember that unlike PHP, you cannot leave a trailing comma in a JSON array/object. 50 | 51 | Manifest entry examples: 52 | 53 | If your repo directory structure is like: 54 | 55 | 56 | └── my_addon 57 | └── pi.my_addon.php 58 | 59 | Manifest Entry: 60 | 61 | "my_addon":{ 62 | "user": "username", 63 | "repo": "reponame" 64 | } 65 | 66 | If your repo directory structure is like this (just the bare addon file in the root of the repo): 67 | 68 | 69 | └── pi.my_addon.php 70 | 71 | Manifest Entry: 72 | 73 | "my_addon": { 74 | "user": "username", 75 | "repo": "reponame", 76 | "add_folder": true, 77 | "stars": 10 78 | } 79 | 80 | If your repo directory structure is like: 81 | 82 | 83 | └── system 84 | └── expressionengine 85 | └── third_party 86 | └── my_addon 87 | └── pi.my_addon.php 88 | 89 | Manifest Entry: 90 | 91 | "my_addon": { 92 | "user": "username", 93 | "repo": "reponame", 94 | "addon_path": "system/expressionengine/third_party/", 95 | "stars": 10 96 | } 97 | 98 | If your repo directory structure is like: 99 | 100 | 101 | ├── ee2 102 | │ └── third_party 103 | │ └── my_addon 104 | │ └── pi.my_addon.php 105 | └── themes 106 | └── third_party 107 | └── my_addon 108 | └── my_addon.css 109 | 110 | Manifest Entry: 111 | 112 | "my_addon": { 113 | "user": "username", 114 | "repo": "reponame", 115 | "addon_path": "ee2/third_party/", 116 | "theme_path": "themes/third_party/" 117 | } 118 | -------------------------------------------------------------------------------- /system/expressionengine/third_party/github_addon_installer/src/Repo.php: -------------------------------------------------------------------------------- 1 | api = $api; 74 | 75 | $this->user = $user; 76 | $this->repo = $repo; 77 | $this->branch = $branch; 78 | 79 | $data = $this->api->fetchJson('repos', $this->user, $this->repo, 'branches'); 80 | 81 | if (empty($data)) { 82 | throw new \Exception(sprintf('Repo %s not found', 'https://github.com/'.$this->user.'/'.$this->repo, $this->repo)); 83 | } 84 | 85 | $branch = null; 86 | 87 | foreach ($data as $row) { 88 | if ($row->name === $this->branch) { 89 | $branch = $row; 90 | break; 91 | } 92 | } 93 | 94 | if (! $branch) { 95 | throw new \Exception(sprintf('Branch %s not found', 'https://github.com/'.$this->user.'/'.$this->repo.'/tree/'.$this->branch, $this->branch)); 96 | } 97 | 98 | $this->sha = $branch->commit->sha; 99 | 100 | /* @TODO use this later */ 101 | /* 102 | $data = $this->api->fetchJson('repos', 'show', $this->user, $this->repo, 'tags'); 103 | 104 | $this->tags = (isset($data->tags)) ? (array) $data->tags : FALSE; 105 | */ 106 | 107 | $data = $this->api->fetchJson('repos', $this->user, $this->repo); 108 | 109 | if (is_object($data)) { 110 | foreach ((array) $data as $property => $value) { 111 | $this->$property = $value; 112 | } 113 | } 114 | } 115 | 116 | public function getFiles() 117 | { 118 | $files = array(); 119 | 120 | $data = $this->api->fetchJson('repos', $this->user, $this->repo, 'git', 'trees', $this->sha, array('recursive' => true)); 121 | 122 | if (isset($data->tree)) { 123 | foreach ($data->tree as $file) { 124 | if ($file->type !== 'tree') { 125 | $files[] = $file; 126 | } 127 | } 128 | } 129 | 130 | return $files; 131 | } 132 | 133 | public function getZipball() 134 | { 135 | return $this->api->fetchProgress('repos', $this->user, $this->repo, 'zipball', $this->branch); 136 | } 137 | 138 | public function getBlob($sha) 139 | { 140 | return $this->api->fetchJson('repos', $this->user, $this->repo, 'git', 'blobs', $sha); 141 | } 142 | 143 | public function getTrees($recursive = false) 144 | { 145 | return $this->api->fetchJson('repos', $this->user, $this->repo, 'git', 'trees', $this->sha, array('recursive' => $recursive)); 146 | } 147 | 148 | public function getUser() 149 | { 150 | return $this->user; 151 | } 152 | 153 | public function getRepo() 154 | { 155 | return $this->repo; 156 | } 157 | 158 | public function getBranch() 159 | { 160 | return $this->branch; 161 | } 162 | 163 | public function getSha() 164 | { 165 | return $this->sha; 166 | } 167 | 168 | public function isFileIgnored($filename) 169 | { 170 | foreach ($this->ignore as $ignore) { 171 | if (strncasecmp($filename, $ignore, strlen($ignore)) === 0) { 172 | return true; 173 | } 174 | } 175 | 176 | return false; 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /system/expressionengine/third_party/github_addon_installer/src/Api.php: -------------------------------------------------------------------------------- 1 | 'EE Github Addon Installer', 13 | CURLOPT_RETURNTRANSFER => true, 14 | CURLOPT_FOLLOWLOCATION => true, 15 | CURLOPT_SSL_VERIFYPEER => false,//@TODO remove this 16 | ); 17 | 18 | protected $basicAuthUsername; 19 | 20 | protected $basicAuthPassword; 21 | 22 | protected $output; 23 | 24 | protected $progress; 25 | 26 | protected $progressBar; 27 | 28 | public function setBasicAuth($username, $password) 29 | { 30 | $this->basicAuthUsername = $username; 31 | $this->basicAuthPassword = $password; 32 | 33 | return $this; 34 | } 35 | 36 | public function setOutput(OutputInterface $output) 37 | { 38 | $this->output = $output; 39 | } 40 | 41 | public function setProgressHelper(ProgressHelper $progress) 42 | { 43 | $this->progress = $progress; 44 | } 45 | 46 | public function setProgressBar(ProgressBar $progressBar) 47 | { 48 | $this->progressBar = $progressBar; 49 | } 50 | 51 | protected function buildUrl(array $segments, array $queryString = null) 52 | { 53 | $url = 'https://api.github.com/'.implode('/', $segments); 54 | 55 | if ($queryString) { 56 | $url .= '?'.http_build_query($queryString); 57 | } 58 | 59 | return $url; 60 | } 61 | 62 | /** 63 | * Fetch raw data from a github url 64 | * 65 | * @param string $segment,... unlimited number of segments 66 | * @param array $params an optional array of query string params 67 | * @return mixed 68 | */ 69 | public function fetch() 70 | { 71 | $segments = func_get_args(); 72 | 73 | $queryString = is_array(end($segments)) ? array_pop($segments) : null; 74 | 75 | $url = $this->buildUrl($segments, $queryString); 76 | 77 | return $this->curl($url); 78 | } 79 | 80 | public function fetchProgress() 81 | { 82 | $segments = func_get_args(); 83 | 84 | $queryString = is_array(end($segments)) ? array_pop($segments) : null; 85 | 86 | $url = $this->buildUrl($segments, $queryString); 87 | 88 | $progress = $this->progress ? $this->progress : $this->progressBar; 89 | 90 | if ($progress && $this->output) { 91 | $output = $this->output; 92 | $current = 0; 93 | 94 | if ($progress instanceof ProgressBar) { 95 | $progress->start(100); 96 | } else { 97 | $progress->start($output, 100); 98 | } 99 | 100 | $return = $this->curl($url, function($downloadSize, $downloadedSize) use ($progress, $output) { 101 | $current = $downloadSize !== 0 ? round($downloadedSize / $downloadSize * 100) : 0; 102 | 103 | if ($progress instanceof ProgressBar) { 104 | $progress->setProgress($current); 105 | } else { 106 | $progress->setCurrent($current); 107 | } 108 | }); 109 | 110 | $progress->finish(); 111 | 112 | return $return; 113 | } 114 | 115 | return $this->curl($url); 116 | } 117 | 118 | /** 119 | * Fetch json data from the github v3 api 120 | * 121 | * @param string $segment,... unlimited number of segments 122 | * @param array $params an optional array of query string params 123 | * @return mixed|null 124 | */ 125 | public function fetchJson() 126 | { 127 | $segments = func_get_args(); 128 | 129 | $queryString = is_array(end($segments)) ? array_pop($segments) : null; 130 | 131 | $url = $this->buildUrl($segments, $queryString); 132 | 133 | $data = $this->curl($url); 134 | 135 | return ($data) ? @json_decode($data) : null; 136 | } 137 | 138 | protected function curl($url, \Closure $progressCallback = null) 139 | { 140 | $ch = curl_init($url); 141 | 142 | curl_setopt_array($ch, $this->curlOptions); 143 | 144 | if ($this->basicAuthUsername && $this->basicAuthPassword) { 145 | curl_setopt($ch, CURLOPT_USERPWD, $this->basicAuthUsername.':'.$this->basicAuthPassword); 146 | } 147 | 148 | if (is_callable($progressCallback)) { 149 | curl_setopt($ch, CURLOPT_NOPROGRESS, false); 150 | curl_setopt($ch, CURLOPT_PROGRESSFUNCTION, $progressCallback); 151 | } 152 | 153 | $data = curl_exec($ch); 154 | 155 | $status = curl_getinfo($ch, CURLINFO_HTTP_CODE); 156 | 157 | if ($status === 403) { 158 | $json = json_decode($data); 159 | 160 | throw new \Exception($json->message); 161 | } elseif ($status !== 200) { 162 | $data = false; 163 | } 164 | 165 | curl_close($ch); 166 | 167 | return $data; 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /system/expressionengine/third_party/github_addon_installer/mcp.github_addon_installer.php: -------------------------------------------------------------------------------- 1 | config->item('github_addon_installer_disabled')) 49 | { 50 | show_error(lang('unauthorized_access')); 51 | } 52 | 53 | $this->base = BASE.AMP.'C=addons_modules'.AMP.'M=show_module_cp'.AMP.'module=github_addon_installer'; 54 | 55 | // no autoloader 56 | if (version_compare(APP_VER, '2.9', '<')) 57 | { 58 | require_once PATH_THIRD.'github_addon_installer/src/Application.php'; 59 | require_once PATH_THIRD.'github_addon_installer/src/Api.php'; 60 | require_once PATH_THIRD.'github_addon_installer/src/Repo.php'; 61 | require_once PATH_THIRD.'github_addon_installer/src/Installer/Installer.php'; 62 | require_once PATH_THIRD.'github_addon_installer/src/Installer/AbstractZipInstaller.php'; 63 | require_once PATH_THIRD.'github_addon_installer/src/Installer/FileInstaller.php'; 64 | require_once PATH_THIRD.'github_addon_installer/src/Installer/SystemUnzipInstaller.php'; 65 | require_once PATH_THIRD.'github_addon_installer/src/Installer/ZipArchiveInstaller.php'; 66 | } 67 | else 68 | { 69 | if ( ! class_exists('Autoloader')) { 70 | require_once APPPATH.'../EllisLab/ExpressionEngine/Core/Autoloader.php'; 71 | } 72 | 73 | Autoloader::getInstance()->addPrefix('eecli\\GithubAddonInstaller', PATH_THIRD.'github_addon_installer/src/'); 74 | } 75 | 76 | $temp_path = APPPATH.'cache/github_addon_installer/'; 77 | 78 | if (! is_dir($temp_path)) { 79 | mkdir($temp_path); 80 | } 81 | 82 | $this->application = new Application(PATH_THIRD, PATH_THIRD_THEMES, $temp_path); 83 | 84 | $this->manifest = $this->application->getManifest(); 85 | } 86 | 87 | public function index() 88 | { 89 | ee()->view->cp_page_title = ee()->lang->line('github_addon_installer_module_name'); 90 | 91 | ee()->load->library('addons'); 92 | 93 | $vars = array(); 94 | $vars['addons'] = array(); 95 | 96 | foreach ($this->manifest as $addon => $params) 97 | { 98 | $name = (isset($params['name'])) ? $params['name'] : $addon; 99 | $description = (isset($params['description'])) ? br().$params['description'] : ''; 100 | //$status = (in_array($addon, $current_addons)) ? lang('addon_installed') : lang('addon_not_installed'); 101 | $status = (ee()->addons->is_package($addon)) ? lang('addon_installed') : lang('addon_not_installed'); 102 | 103 | //$install = (in_array($addon, $current_addons)) ? lang('addon_install') : lang('addon_reinstall'); 104 | 105 | $url = 'https://github.com/'.$params['user'].'/'.$params['repo']; 106 | 107 | if (isset($params['branch'])) 108 | { 109 | $url .= '/tree/'.$params['branch']; 110 | } 111 | 112 | $branch = isset($params['branch']) ? $params['branch'] : 'master'; 113 | 114 | $vars['addons'][] = array( 115 | 'name' => $name,//.$description, 116 | 'github_url' => anchor($url, $url, 'rel="external"'), 117 | 'branch' => form_input("", $branch, 'class="branch '.$addon.'-branch"'), 118 | 'author' => $params['user'], 119 | 'stars' => empty($params['stars']) ? '0' : (string) $params['stars'], 120 | 'status' => $status, 121 | 'install' => anchor($this->base.AMP.'method=install'.AMP.'addon='.$addon, lang('addon_install'), 'data-addon="'.$addon.'"') 122 | ); 123 | } 124 | 125 | ee()->load->library('javascript'); 126 | 127 | ee()->javascript->output(' 128 | $("table#addons").tablesorter({ 129 | headers: {1: {sorter: false}, 6: {sorter: false}}, 130 | widgets: ["zebra"] 131 | }); 132 | $("table#addons tr td.addon_install a").click(function(){ 133 | var a = $(this); 134 | var tds = a.parents("tr").children("td"); 135 | var statusTd = a.parents("td").siblings("td.addon_status"); 136 | var originalColor = tds.css("backgroundColor"); 137 | var originalText = a.text(); 138 | tds.animate({backgroundColor:"#d0d0d0"}); 139 | a.html("'.lang('addon_installing').'"); 140 | $.get( 141 | $(this).attr("href"), 142 | {branch: $("."+$(this).data("addon")+"-branch").val()}, 143 | function(data){ 144 | tds.animate({backgroundColor:originalColor}); 145 | a.html(originalText); 146 | if (data.message_success) { 147 | if (data.redirect) { 148 | window.location.href = data.redirect; 149 | return; 150 | } 151 | statusTd.html("'.lang('addon_installed').'"); 152 | $.ee_notice(data.message_success, {"type":"success"}); 153 | } else { 154 | $.ee_notice(data.message_failure, {"type":"error"}); 155 | //td.animate({backgroundColor:"red"}); 156 | } 157 | }, 158 | "json" 159 | ); 160 | return false; 161 | }); 162 | $("select#addonFilter").change(function(){ 163 | var filter = $(this).val(); 164 | $("#addonKeyword").hide(); 165 | $("table#addons tbody tr").show(); 166 | if (filter == "") { 167 | $("table#addons tbody tr").show(); 168 | } else if (filter == "keyword") { 169 | $("#addonKeyword").val("").show().focus(); 170 | } else { 171 | $("td."+$(this.options[this.selectedIndex]).parents("optgroup").data("filter")).filter(function(){ 172 | return $(this).text() != filter; 173 | }).parents("tr").hide(); 174 | } 175 | }); 176 | //add all values from the table to filter 177 | $("select#addonFilter optgroup").each(function(index, element){ 178 | var values = []; 179 | $("td."+$(this).data("filter")).each(function(){ 180 | if ($.inArray($(this).text(), values) === -1) { 181 | values.push($(this).text()); 182 | } 183 | }); 184 | //case insensitive sort 185 | values.sort(function(a, b){ 186 | a = a.toLowerCase(); 187 | b = b.toLowerCase(); 188 | if (a > b) { 189 | return 1; 190 | } 191 | if (a < b) { 192 | return -1; 193 | } 194 | return 0; 195 | }); 196 | for (i in values) { 197 | $(element).append($("