├── README.md ├── administrator └── components │ └── com_patchtester │ ├── access.xml │ ├── backups │ └── index.html │ ├── config.xml │ ├── controller.php │ ├── controllers │ ├── index.html │ └── pull.php │ ├── index.html │ ├── language │ ├── de-DE │ │ ├── de-DE.com_patchtester.ini │ │ ├── de-DE.com_patchtester.sys.ini │ │ └── index.html │ ├── en-GB │ │ ├── en-GB.com_patchtester.ini │ │ ├── en-GB.com_patchtester.sys.ini │ │ └── index.html │ └── index.html │ ├── models │ ├── index.html │ ├── pull.php │ └── pulls.php │ ├── patchtester.php │ ├── patchtester.xml │ ├── sql │ ├── index.html │ ├── install.mysql.utf8.sql │ └── uninstall.mysql.utf8.sql │ ├── tables │ ├── index.html │ └── tests.php │ └── views │ ├── index.html │ └── pulls │ ├── index.html │ ├── tmpl │ ├── default.php │ └── index.html │ └── view.html.php ├── components └── com_patchtester │ ├── index.html │ └── patchtester.php ├── libraries └── joomla │ └── client │ ├── curl.php │ ├── github.php │ ├── github │ ├── cacert.pem │ ├── githubgists.php │ ├── githubissues.php │ └── githubpulls.php │ └── githubobject.php ├── patchtester ├── build.sh ├── github.xml └── pkg_patchtester.xml └── updates ├── github.xml └── patchtester.xml /README.md: -------------------------------------------------------------------------------- 1 | Patch Tester 2 | ============= 3 | 4 | Easily apply changes from pull requests. 5 | 6 | 1. Download files into Joomla install. 7 | 2. Perform discover install. 8 | 3. Go to Component Options and set organization name and repository name 9 | 10 | Click Apply Patch to apply the proposed changes from the pull request. 11 | Click Revert Patch to revert an applied patch. 12 | -------------------------------------------------------------------------------- /administrator/components/com_patchtester/access.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 |
7 |
8 | -------------------------------------------------------------------------------- /administrator/components/com_patchtester/backups/index.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ianmacl/patchtester/82190958850f002bc7aad38aa3ea2d18d4ea6cd8/administrator/components/com_patchtester/backups/index.html -------------------------------------------------------------------------------- /administrator/components/com_patchtester/config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 |
7 | 8 | 13 | 14 | 19 | 20 |
21 | 22 |
26 | 27 | 33 |
34 |
35 | -------------------------------------------------------------------------------- /administrator/components/com_patchtester/controller.php: -------------------------------------------------------------------------------- 1 | getModel('pull') 24 | ->apply(JRequest::getVar('pull_id')); 25 | 26 | $msg = 'Patch successfully applied'; 27 | $type = 'message'; 28 | } catch (Exception $e) { 29 | $msg = $e->getMessage(); 30 | $type = 'error'; 31 | } 32 | 33 | $this->setRedirect(JRoute::_('index.php?option=com_patchtester&view=pulls', false), $msg, $type); 34 | } 35 | 36 | public function revert() 37 | { 38 | $model = $this->getModel('pull'); 39 | if ($model->revert(JRequest::getVar('pull_id'))) { 40 | $msg = 'Patch successfully reverted'; 41 | $type = 'message'; 42 | } else { 43 | $msg = 'Patch did not revert'; 44 | $type = 'error'; 45 | } 46 | $this->setRedirect(JRoute::_('index.php?option=com_patchtester&view=pulls', false), $msg, $type); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /administrator/components/com_patchtester/index.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ianmacl/patchtester/82190958850f002bc7aad38aa3ea2d18d4ea6cd8/administrator/components/com_patchtester/index.html -------------------------------------------------------------------------------- /administrator/components/com_patchtester/language/de-DE/de-DE.com_patchtester.ini: -------------------------------------------------------------------------------- 1 | COM_PATCHTESTER_NOT_APPLIED="Nicht angebracht" 2 | COM_PATCHTESTER_APPLIED="Angebracht" 3 | COM_PATCHTESTER_REVERT_PATCH="Patch zurücknehmen" 4 | COM_PATCHTESTER_APPLY_PATCH="Patch anbringen" 5 | COM_PATCHTESTER_TEST_THIS_PATCH="Diesen Patch testen" 6 | COM_PATCHTESTER_COMPONENT_LABEL="Patch Tester" 7 | COM_PATCHTESTER_COMPONENT_DESC="Patch Tester Konfiguration" 8 | COM_PATCHTESTER_FIELD_ORG_LABEL="Github Benutzername" 9 | COM_PATCHTESTER_FIELD_ORG_DESC="Name des Github Kontos von welchem Pull Requests beobachtet werden sollen." 10 | COM_PATCHTESTER_FIELD_REPO_LABEL="Github Repository" 11 | COM_PATCHTESTER_FIELD_REPO_DESC="Name des Github Repositories von welchem Pull Requests beobachtet werden sollen." 12 | COM_PATCHTESTER_JOOMLACODE_ISSUE="Joomlacode Tracker" 13 | COM_PATCHTESTER_PULL_ID="Pull ID" 14 | 15 | ;messages 16 | COM_PATCHTESTER_REPO_IS_GONE="Der Patch konnte nicht angebracht werden weil das Repository fehlt" 17 | COM_PATCHTESTER_CONFLICT_S="Der Patch konnte nicht angebracht werden weil er mit einem bereits angebrachten Patch in Konflikt steht: %s" 18 | COM_PATCHTESTER_FILE_DELETED_DOES_NOT_EXIST_S="Die zu löschende Datei existiert nicht: %s" 19 | COM_PATCHTESTER_FILE_MODIFIED_DOES_NOT_EXIST_S="Die zu ändernde Datei existiert nicht: %s" 20 | -------------------------------------------------------------------------------- /administrator/components/com_patchtester/language/de-DE/de-DE.com_patchtester.sys.ini: -------------------------------------------------------------------------------- 1 | COM_PATCHTESTER="Patch Tester" 2 | COM_PATCHTESTER_XML_DESCRIPTION="Komponente um die Verwaltung von Pull Requests zu testen" 3 | 4 | -------------------------------------------------------------------------------- /administrator/components/com_patchtester/language/de-DE/index.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ianmacl/patchtester/82190958850f002bc7aad38aa3ea2d18d4ea6cd8/administrator/components/com_patchtester/language/de-DE/index.html -------------------------------------------------------------------------------- /administrator/components/com_patchtester/language/en-GB/en-GB.com_patchtester.ini: -------------------------------------------------------------------------------- 1 | COM_PATCHTESTER_NOT_APPLIED="Not Applied" 2 | COM_PATCHTESTER_APPLIED="Applied" 3 | COM_PATCHTESTER_REVERT_PATCH="Revert Patch" 4 | COM_PATCHTESTER_APPLY_PATCH="Apply Patch" 5 | COM_PATCHTESTER_TEST_THIS_PATCH="Test This Patch" 6 | COM_PATCHTESTER_COMPONENT_LABEL="Patch Tester" 7 | COM_PATCHTESTER_COMPONENT_DESC="Patch Tester Configuration Values" 8 | COM_PATCHTESTER_FIELD_ORG_LABEL="Github Username" 9 | COM_PATCHTESTER_FIELD_ORG_DESC="Name of account on Github of which to monitor pull requests" 10 | COM_PATCHTESTER_FIELD_REPO_LABEL="Github Repository" 11 | COM_PATCHTESTER_FIELD_REPO_DESC="Name of repository on Github of which to monitor pull requests" 12 | COM_PATCHTESTER_JOOMLACODE_ISSUE="Joomlacode Issue" 13 | COM_PATCHTESTER_PULL_ID="Pull ID" 14 | 15 | ;messages 16 | COM_PATCHTESTER_REPO_IS_GONE="The patch could not be applied because the repository is missing" 17 | COM_PATCHTESTER_CONFLICT_S="The patch could not be applied because it conflicts with a previously applied patch: %s" 18 | COM_PATCHTESTER_FILE_DELETED_DOES_NOT_EXIST_S="The file marked for deletion does not exist: %s" 19 | COM_PATCHTESTER_FILE_MODIFIED_DOES_NOT_EXIST_S="The file marked for modification does not exist: %s" 20 | -------------------------------------------------------------------------------- /administrator/components/com_patchtester/language/en-GB/en-GB.com_patchtester.sys.ini: -------------------------------------------------------------------------------- 1 | COM_PATCHTESTER="Patch Tester" 2 | COM_PATCHTESTER_XML_DESCRIPTION="Component for pull request management testing" 3 | 4 | -------------------------------------------------------------------------------- /administrator/components/com_patchtester/language/en-GB/index.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ianmacl/patchtester/82190958850f002bc7aad38aa3ea2d18d4ea6cd8/administrator/components/com_patchtester/language/en-GB/index.html -------------------------------------------------------------------------------- /administrator/components/com_patchtester/language/index.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ianmacl/patchtester/82190958850f002bc7aad38aa3ea2d18d4ea6cd8/administrator/components/com_patchtester/language/index.html -------------------------------------------------------------------------------- /administrator/components/com_patchtester/models/index.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ianmacl/patchtester/82190958850f002bc7aad38aa3ea2d18d4ea6cd8/administrator/components/com_patchtester/models/index.html -------------------------------------------------------------------------------- /administrator/components/com_patchtester/models/pull.php: -------------------------------------------------------------------------------- 1 | setState('params', $params); 30 | $this->setState('github_user', $params->get('org', 'joomla')); 31 | $this->setState('github_repo', $params->get('repo', 'joomla-cms')); 32 | 33 | parent::populateState(); 34 | } 35 | 36 | protected function parsePatch($patch) 37 | { 38 | $state = 0; 39 | $files = array(); 40 | 41 | $lines = explode("\n", $patch); 42 | 43 | foreach ($lines AS $line) { 44 | switch ($state) 45 | { 46 | case 0: 47 | if (strpos($line, 'diff --git') === 0) { 48 | $state = 1; 49 | } 50 | $file = new stdClass; 51 | $file->action = 'modified'; 52 | break; 53 | 54 | case 1: 55 | if (strpos($line, 'index') === 0) { 56 | $file->index = substr($line, 6); 57 | } 58 | 59 | if (strpos($line, '---') === 0) { 60 | $file->old = substr($line, 6); 61 | } 62 | 63 | if (strpos($line, '+++') === 0) { 64 | $file->new = substr($line, 6); 65 | } 66 | 67 | if (strpos($line, 'new file mode') === 0) { 68 | $file->action = 'added'; 69 | } 70 | 71 | if (strpos($line, 'deleted file mode') === 0) { 72 | $file->action = 'deleted'; 73 | } 74 | 75 | if (strpos($line, '@@') === 0) { 76 | $state = 0; 77 | $files[] = $file; 78 | } 79 | break; 80 | } 81 | } 82 | return $files; 83 | } 84 | 85 | public function apply($id) 86 | { 87 | jimport('joomla.client.github'); 88 | jimport('joomla.client.curl'); 89 | 90 | $table = JTable::getInstance('tests', 'PatchTesterTable'); 91 | $github = new JGithub; 92 | $pull = $github->pulls->get($this->getState('github_user'), $this->getState('github_repo'), $id); 93 | 94 | if (is_null($pull->head->repo)) { 95 | throw new Exception(JText::_('COM_PATCHTESTER_REPO_IS_GONE')); 96 | } 97 | 98 | $patch = JCurl::getAdapter($pull->diff_url) 99 | ->fetch()->body; 100 | 101 | $files = $this->parsePatch($patch); 102 | 103 | foreach($files AS $file) { 104 | if ($file->action == 'deleted' && ! file_exists(JPATH_ROOT . '/' . $file->old)) { 105 | throw new Exception(sprintf(JText::_('COM_PATCHTESTER_FILE_DELETED_DOES_NOT_EXIST_S'), $file->old)); 106 | } 107 | if ($file->action == 'added' || $file->action == 'modified') { 108 | 109 | // if the backup file already exists, we can't apply the patch 110 | if (file_exists(JPATH_COMPONENT . '/backups/' . md5($file->new) . '.txt')) { 111 | throw new Exception(sprintf(JText::_('COM_PATCHTESTER_CONFLICT_S'), $file->new)); 112 | } 113 | 114 | if ($file->action == 'modified' && ! file_exists(JPATH_ROOT . '/' . $file->old)) { 115 | throw new Exception(sprintf(JText::_('COM_PATCHTESTER_FILE_MODIFIED_DOES_NOT_EXIST_S'), $file->old)); 116 | } 117 | 118 | $url = 'https://raw.github.com/' . $pull->head->user->login . '/' . $pull->head->repo->name . '/' . 119 | $pull->head->ref . '/' . $file->new; 120 | 121 | $file->body = JCurl::getAdapter($url) 122 | ->fetch()->body; 123 | } 124 | } 125 | 126 | // at this point, we have ensured that we have all the new files and there are no conflicts 127 | 128 | foreach ($files AS $file) 129 | { 130 | // we only create a backup if the file already exists 131 | if ($file->action == 'deleted' || (file_exists(JPATH_ROOT . '/' . $file->new) && $file->action == 'modified')) { 132 | if( ! JFile::copy(JPath::clean(JPATH_ROOT . '/' . $file->old), JPATH_COMPONENT . '/backups/' . md5($file->old) . '.txt')) { 133 | throw new Exception(sprintf('Can not copy file %s to %s' 134 | , JPATH_ROOT . '/' . $file->old, JPATH_COMPONENT . '/backups/' . md5($file->old) . '.txt')); 135 | } 136 | } 137 | 138 | switch ($file->action) 139 | { 140 | case 'modified': 141 | case 'added': 142 | if( ! JFile::write(JPath::clean(JPATH_ROOT . '/' . $file->new), $file->body)) { 143 | throw new Exception(sprintf('Can not write the file: %s', JPATH_ROOT . '/' . $file->new)); 144 | } 145 | break; 146 | 147 | case 'deleted': 148 | if( ! JFile::delete(JPATH::clean(JPATH_ROOT . '/' . $file->old))) { 149 | throw new Exception(sprintf('Can not delete the file: %s', JPATH_ROOT . '/' . $file->old)); 150 | } 151 | break; 152 | } 153 | } 154 | 155 | $table->pull_id = $pull->number; 156 | $table->data = json_encode($files); 157 | $table->patched_by = JFactory::getUser()->id; 158 | $table->applied = 1; 159 | $version = new JVersion; 160 | $table->applied_version = $version->getShortVersion(); 161 | 162 | if ( ! $table->store()) { 163 | throw new Exception($table->getError()); 164 | } 165 | 166 | return true; 167 | } 168 | 169 | public function revert($id) 170 | { 171 | $table = JTable::getInstance('tests', 'PatchTesterTable'); 172 | 173 | $table->load($id); 174 | 175 | // we don't want to restore files from an older version 176 | $version = new JVersion; 177 | if ($table->applied_version != $version->getShortVersion()) { 178 | $table->applied = 0; 179 | $table->applied_version = ''; 180 | $table->store(); 181 | return true; 182 | } 183 | 184 | $files = json_decode($table->data); 185 | 186 | foreach ($files AS $file) { 187 | switch ($file->action) { 188 | case 'deleted': 189 | case 'modified': 190 | if ( ! JFile::copy(JPATH_COMPONENT . '/backups/' . md5($file->old) . '.txt', JPATH_ROOT . '/' . $file->old)) { 191 | throw new Exception(sprintf('Can not copy file %s to %s' 192 | , JPATH_COMPONENT . '/backups/' . md5($file->old) . '.txt' 193 | , JPATH_ROOT . '/' . $file->old)); 194 | } 195 | if ( ! JFile::delete(JPATH_COMPONENT . '/backups/' . md5($file->old) . '.txt')) { 196 | throw new Exception(sprintf('Can not delete the file: %s' 197 | , JPATH_COMPONENT . '/backups/' . md5($file->old) . '.txt')); 198 | } 199 | break; 200 | 201 | case 'added': 202 | if ( ! JFile::delete(JPath::clean(JPATH_ROOT . '/' . $file->new))) { 203 | throw new Exception(sprintf('Can not delete the file: %s', JPATH_ROOT . '/' . $file->new)); 204 | } 205 | break; 206 | } 207 | } 208 | 209 | $table->applied_version = ''; 210 | $table->applied = 0; 211 | $table->store(); 212 | 213 | return true; 214 | } 215 | 216 | } 217 | -------------------------------------------------------------------------------- /administrator/components/com_patchtester/models/pulls.php: -------------------------------------------------------------------------------- 1 | getUserStateFromRequest($this->context.'.filter.search', 'filter_search'); 53 | $this->setState('filter.search', $search); 54 | 55 | // Load the parameters. 56 | $params = JComponentHelper::getParams('com_patchtester'); 57 | 58 | $this->setState('params', $params); 59 | $this->setState('github_user', $params->get('org', 'joomla')); 60 | $this->setState('github_repo', $params->get('repo', 'joomla-cms')); 61 | 62 | // List state information. 63 | parent::populateState('number', 'desc'); 64 | } 65 | 66 | /** 67 | * Method to get a store id based on model configuration state. 68 | * 69 | * This is necessary because the model is used by the component and 70 | * different modules that might need different sets of data or different 71 | * ordering requirements. 72 | * 73 | * @param string $id A prefix for the store id. 74 | * @return string A store id. 75 | * @since 1.6 76 | */ 77 | protected function getStoreId($id = '') 78 | { 79 | return parent::getStoreId($id); 80 | } 81 | 82 | public function getAppliedPatches() 83 | { 84 | $query = $this->_db->getQuery(true); 85 | $query->select('*'); 86 | $query->from('#__tests'); 87 | $query->where('applied = 1'); 88 | 89 | $this->_db->setQuery($query); 90 | $tests = $this->_db->loadObjectList('pull_id'); 91 | return $tests; 92 | } 93 | 94 | public function getItems() 95 | { 96 | jimport('joomla.client.github'); 97 | 98 | if ($this->getState('github_user') == '' || $this->getState('github_repo') == '') { 99 | return array(); 100 | } 101 | 102 | $this->ordering = $this->getState('list.ordering', 'title'); 103 | $this->orderDir = $this->getState('list.direction', 'asc'); 104 | $search = $this->getState('filter.search'); 105 | 106 | try { 107 | $github = new JGithub(); 108 | $pulls = $github->pulls->getAll($this->getState('github_user'), $this->getState('github_repo')); 109 | usort($pulls, array($this, 'sortItems')); 110 | 111 | foreach ($pulls AS $i => &$pull) 112 | { 113 | if($search && false === strpos($pull->title, $search)) { 114 | unset($pulls[$i]); 115 | continue; 116 | } 117 | $matches = array(); 118 | preg_match('#\[\#([0-9]+)\]#', $pull->title, $matches); 119 | $pull->joomlacode_issue = isset($matches[1]) ? $matches[1] : 0; 120 | } 121 | 122 | return $pulls; 123 | } catch (Exception $e) { 124 | JFactory::getApplication()->enqueueMessage($e->getMessage(), 'error'); 125 | 126 | return array(); 127 | } 128 | } 129 | 130 | public function sortItems($a, $b) 131 | { 132 | switch ($this->ordering) 133 | { 134 | case 'title' : 135 | return ($this->orderDir == 'asc') ? strcasecmp($a->title, $b->title) : strcasecmp($b->title, $a->title); 136 | 137 | case 'number' : 138 | default : 139 | return ($this->orderDir == 'asc') ? $b->number < $a->number : $b->number > $a->number; 140 | } 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /administrator/components/com_patchtester/patchtester.php: -------------------------------------------------------------------------------- 1 | authorise('core.manage', 'com_patchtester')) { 14 | return JError::raiseWarning(404, JText::_('JERROR_ALERTNOAUTHOR')); 15 | } 16 | 17 | // Include dependencies 18 | jimport('joomla.application.component.controller'); 19 | 20 | $controller = JController::getInstance('PatchTester'); 21 | $controller->execute(JRequest::getCmd('task')); 22 | $controller->redirect(); 23 | -------------------------------------------------------------------------------- /administrator/components/com_patchtester/patchtester.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | com_patchtester 4 | Ian MacLennan 5 | October 2011 6 | (C) 2011 Ian MacLennan. All rights reserved. 7 | 8 | GNU General Public License version 2 or later; see 9 | LICENSE.txt 10 | ianlenmac@gmail.com 11 | http://github.com/ianmacl 12 | 1.0alpha 13 | COM_PATCHTESTER_XML_DESCRIPTION 14 | 15 | 16 | 17 | sql/install.mysql.utf8.sql 18 | 19 | 20 | 21 | 22 | sql/uninstall.mysql.utf8.sql 23 | 24 | 25 | 26 | 27 | patchtester.php 28 | 29 | 30 | com_patchtester 31 | 32 | access.xml 33 | config.xml 34 | controller.php 35 | patchtester.php 36 | controllers 37 | models 38 | sql 39 | tables 40 | views 41 | language 42 | 43 | 44 | 45 | 46 | 47 | https://raw.github.com/ianmacl/patchtester/master/updates/patchtester.xml 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /administrator/components/com_patchtester/sql/index.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ianmacl/patchtester/82190958850f002bc7aad38aa3ea2d18d4ea6cd8/administrator/components/com_patchtester/sql/index.html -------------------------------------------------------------------------------- /administrator/components/com_patchtester/sql/install.mysql.utf8.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS `#__tests` ( 2 | `id` int(11) NOT NULL AUTO_INCREMENT, 3 | `pull_id` int(11) NOT NULL, 4 | `data` varchar(5000) NOT NULL, 5 | `patched_by` int(11) NOT NULL, 6 | `applied` int(11) NOT NULL, 7 | `applied_version` varchar(25) NOT NULL, 8 | `rating` int(11) NOT NULL, 9 | `comments` varchar(3000) NOT NULL, 10 | PRIMARY KEY (`id`) 11 | ) DEFAULT CHARSET=utf8; 12 | -------------------------------------------------------------------------------- /administrator/components/com_patchtester/sql/uninstall.mysql.utf8.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS `#__tests` 2 | -------------------------------------------------------------------------------- /administrator/components/com_patchtester/tables/index.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ianmacl/patchtester/82190958850f002bc7aad38aa3ea2d18d4ea6cd8/administrator/components/com_patchtester/tables/index.html -------------------------------------------------------------------------------- /administrator/components/com_patchtester/tables/tests.php: -------------------------------------------------------------------------------- 1 | escape($this->state->get('list.ordering')); 15 | $listDirn = $this->escape($this->state->get('list.direction')); 16 | 17 | ?> 18 | 24 | 25 |
26 |
27 | 33 |
34 |
35 | 36 | 37 | 38 | 39 | 42 | 45 | 48 | 51 | 54 | 57 | 58 | 59 | 60 | 61 | 63 | 64 | 65 | 66 | items as $i => $item) : 67 | if (isset($this->patches[$item->number])) { 68 | $patch = $this->patches[$item->number]; 69 | } else { 70 | $patch = false; 71 | } 72 | ?> 73 | 74 | 77 | 80 | 83 | 92 | 101 | 110 | 111 | 112 | 113 |
40 | 41 | 43 | 44 | 46 | 47 | 49 | 50 | 52 | 53 | 55 | 56 |
62 |
75 | id); ?> 76 | 78 | number; ?> 79 | 81 | title; ?> 82 | 84 | joomlacode_issue > 0) { 86 | echo ''; 88 | echo '[#' . $item->joomlacode_issue . ']'; 89 | } 90 | ?> 91 | 93 | applied) { 95 | echo JText::_('COM_PATCHTESTER_APPLIED'); 96 | } else { 97 | echo JText::_('COM_PATCHTESTER_NOT_APPLIED'); 98 | } 99 | ?> 100 | 102 | applied) { 104 | echo ''.JText::_('COM_PATCHTESTER_REVERT_PATCH').''; 105 | } else { 106 | echo ''.JText::_('COM_PATCHTESTER_APPLY_PATCH').''; 107 | } 108 | ?> 109 |
114 | 115 |
116 | 117 | 118 | 119 | 120 | 121 | 122 |
123 |
124 | -------------------------------------------------------------------------------- /administrator/components/com_patchtester/views/pulls/tmpl/index.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ianmacl/patchtester/82190958850f002bc7aad38aa3ea2d18d4ea6cd8/administrator/components/com_patchtester/views/pulls/tmpl/index.html -------------------------------------------------------------------------------- /administrator/components/com_patchtester/views/pulls/view.html.php: -------------------------------------------------------------------------------- 1 | state = $this->get('State'); 29 | $this->items = $this->get('Items'); 30 | $this->patches = $this->get('AppliedPatches'); 31 | 32 | // Check for errors. 33 | if (count($errors = $this->get('Errors'))) { 34 | JError::raiseError(500, implode("\n", $errors)); 35 | return false; 36 | } 37 | 38 | $this->addToolbar(); 39 | parent::display($tpl); 40 | } 41 | 42 | /** 43 | * Add the page title and toolbar. 44 | */ 45 | protected function addToolbar() 46 | { 47 | JToolBarHelper::title('Joomla! Patch Tester'); 48 | JToolBarHelper::preferences('com_patchtester'); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /components/com_patchtester/index.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ianmacl/patchtester/82190958850f002bc7aad38aa3ea2d18d4ea6cd8/components/com_patchtester/index.html -------------------------------------------------------------------------------- /components/com_patchtester/patchtester.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ianmacl/patchtester/82190958850f002bc7aad38aa3ea2d18d4ea6cd8/components/com_patchtester/patchtester.php -------------------------------------------------------------------------------- /libraries/joomla/client/curl.php: -------------------------------------------------------------------------------- 1 | curlOptions = (array)$options; 78 | 79 | return $this; 80 | } 81 | 82 | /** 83 | * Read URL contents. 84 | * 85 | * @throws Exception 86 | * 87 | * @return object The cURL response. 88 | */ 89 | public function fetch() 90 | { 91 | $ch = curl_init(); 92 | 93 | curl_setopt_array($ch, $this->curlOptions); 94 | 95 | curl_setopt($ch, CURLOPT_URL, $this->_uri); 96 | 97 | if ( ! array_key_exists(CURLOPT_SSL_VERIFYHOST, $this->curlOptions)) 98 | { 99 | curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, true); 100 | } 101 | 102 | if ( ! array_key_exists(CURLOPT_SSL_VERIFYPEER, $this->curlOptions)) 103 | { 104 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); 105 | } 106 | 107 | if ( ! array_key_exists(CURLOPT_FOLLOWLOCATION, $this->curlOptions)) 108 | { 109 | curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); 110 | } 111 | 112 | if ( ! array_key_exists(CURLOPT_MAXREDIRS, $this->curlOptions)) 113 | { 114 | curl_setopt($ch, CURLOPT_MAXREDIRS, 10); 115 | } 116 | 117 | if ( ! array_key_exists(CURLOPT_TIMEOUT, $this->curlOptions)) 118 | { 119 | curl_setopt($ch, CURLOPT_TIMEOUT, 120); 120 | } 121 | 122 | if ( ! array_key_exists(CURLOPT_RETURNTRANSFER, $this->curlOptions)) 123 | { 124 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 125 | } 126 | 127 | if ($this->target) 128 | { 129 | // Write the response to a file 130 | $fp = fopen($this->target, 'w'); 131 | 132 | if ( ! $fp) 133 | { 134 | throw new Exception('Can not open target file at: '.$this->target); 135 | } 136 | 137 | // Use CURLOPT_FILE to speed things up 138 | curl_setopt($ch, CURLOPT_FILE, $fp); 139 | } 140 | else 141 | { 142 | // Return the response 143 | if ( ! array_key_exists(CURLOPT_RETURNTRANSFER, $this->curlOptions)) 144 | { 145 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 146 | } 147 | } 148 | 149 | $response = curl_exec($ch); 150 | 151 | if (curl_errno($ch)) 152 | { 153 | throw new Exception('Curl Error: '.curl_error($ch)); 154 | } 155 | 156 | $info = curl_getinfo($ch); 157 | 158 | if (isset($info['http_code']) && $info['http_code'] != 200) 159 | { 160 | $response = false; 161 | } 162 | 163 | curl_close($ch); 164 | 165 | $return = JArrayHelper::toObject($info); 166 | $return->body = $response; 167 | 168 | return $return; 169 | } 170 | 171 | /** 172 | * Save the response to a file. 173 | * 174 | * @param string $target Target path 175 | * 176 | * @return boolean true on success 177 | * 178 | * @throws Exception 179 | */ 180 | public function saveToFile($target) 181 | { 182 | $this->target = $target; 183 | 184 | $response = $this->fetch(); 185 | 186 | if (false === $response) 187 | { 188 | throw new Exception('File cannot be downloaded'); 189 | } 190 | 191 | return true; 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /libraries/joomla/client/github.php: -------------------------------------------------------------------------------- 1 | credentials['username'] = $options['username']; 65 | $this->credentials['password'] = $options['password']; 66 | $this->authentication_method = JGithub::AUTHENTICATION_BASIC; 67 | } elseif (isset($options['token'])) { 68 | $this->credentials['token'] = $options['token']; 69 | $this->authentication_method = JGithub::AUTHENTICATION_OAUTH; 70 | } else { 71 | $this->authentication_method = JGithub::AUTHENTICATION_NONE; 72 | } 73 | } 74 | 75 | public function __get($name) 76 | { 77 | if ($name == 'gists') { 78 | if ($this->gists == null) { 79 | $this->gists = new JGithubGists($this); 80 | } 81 | return $this->gists; 82 | } 83 | 84 | if ($name == 'issues') { 85 | if ($this->issues == null) { 86 | $this->issues = new JGithubIssues($this); 87 | } 88 | return $this->issues; 89 | } 90 | 91 | if ($name == 'pulls') { 92 | if ($this->pulls == null) { 93 | $this->pulls = new JGithubPulls($this); 94 | } 95 | return $this->pulls; 96 | } 97 | } 98 | 99 | public function sendRequest($url, $method = 'get', $data = array(), $options = array()) 100 | { 101 | $curl_options = array( 102 | CURLOPT_HEADER => false, 103 | CURLOPT_FOLLOWLOCATION => false, 104 | CURLOPT_USERAGENT => 'JGithub', 105 | CURLOPT_CONNECTTIMEOUT => 120, 106 | CURLINFO_HEADER_OUT => true, 107 | CURLOPT_HTTPHEADER => array('Content-type: application/json'), 108 | CURLOPT_CAINFO => dirname(__FILE__) . '/github/cacert.pem', 109 | CURLOPT_SSL_VERIFYPEER => true, 110 | CURLOPT_SSL_VERIFYHOST => 2, 111 | ); 112 | 113 | switch ($this->authentication_method) 114 | { 115 | case JGithub::AUTHENTICATION_BASIC: 116 | $curl_options[CURLOPT_USERPWD] = $this->credentials['username'].':'.$this->credentials['password']; 117 | break; 118 | 119 | case JGithub::AUTHENTICATION_OAUTH: 120 | if (strpos($url, '?') === false) { 121 | $url .= '?access_token='.$this->credentials['token']; 122 | } else { 123 | $url .= '&access_token='.$this->credentials['token']; 124 | } 125 | break; 126 | } 127 | 128 | switch ($method) { 129 | case 'post': 130 | $curl_options[CURLOPT_POST] = 1; 131 | $curl_options[CURLOPT_POSTFIELDS] = json_encode($data); 132 | break; 133 | 134 | case 'put': 135 | $curl_options[CURLOPT_POST] = 1; 136 | $curl_options[CURLOPT_POSTFIELDS] = ''; 137 | $curl_options[CURLOPT_CUSTOMREQUEST] = 'PUT'; 138 | $curl_options[CURLOPT_HTTPGET] = false; 139 | break; 140 | 141 | case 'patch': 142 | $curl_options[CURLOPT_POSTFIELDS] = json_encode($data); 143 | 144 | case 'delete': 145 | $curl_options[CURLOPT_CUSTOMREQUEST] = strtoupper($method); 146 | $curl_options[CURLOPT_POST] = false; 147 | $curl_options[CURLOPT_HTTPGET] = false; 148 | break; 149 | 150 | case 'get': 151 | $curl_options[CURLOPT_POSTFIELDS] = null; 152 | $curl_options[CURLOPT_POST] = false; 153 | $curl_options[CURLOPT_HTTPGET] = true; 154 | break; 155 | } 156 | 157 | $curlResponse = JCurl::getAdapter('https://api.github.com'.$url) 158 | ->setOptions($curl_options)->fetch(); 159 | 160 | $response = new JHttpResponse; 161 | 162 | $response->code = $curlResponse->http_code; 163 | $response->headers = $curlResponse->request_header; 164 | $response->body = json_decode($curlResponse->body); 165 | 166 | return $response; 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /libraries/joomla/client/github/githubgists.php: -------------------------------------------------------------------------------- 1 | connector = $connector; 41 | } 42 | 43 | protected function paginate($url, $page = 0, $per_page = 0) 44 | { 45 | //TODO: Make a new base class and move paginate into it 46 | $query_string = array(); 47 | 48 | if ($page > 0) { 49 | $query_string[] = 'page='.(int)$page; 50 | } 51 | 52 | if ($per_page > 0) { 53 | $query_string[] = 'per_page='.(int)$per_page; 54 | } 55 | 56 | if (isset($query_string[0])) { 57 | $query = implode('&', $query_string); 58 | } else { 59 | $query = ''; 60 | } 61 | 62 | if (strlen($query) > 0) { 63 | if (strpos($url, '?') === false) { 64 | $url .= '?'.$query; 65 | } else { 66 | $url .= '&'.$query; 67 | } 68 | } 69 | 70 | return $url; 71 | } 72 | 73 | public function getAll($page = 0, $per_page = 0) 74 | { 75 | $url = '/gists'; 76 | return $this->connector->sendRequest($this->paginate($url, $page, $per_page))->body; 77 | } 78 | 79 | public function getByUser($user, $page = 0, $per_page = 0) 80 | { 81 | $url = '/users/'.$user.'/gists'; 82 | return $this->connector->sendRequest($this->paginate($url, $page, $per_page))->body; 83 | } 84 | 85 | public function getPublic($page = 0, $per_page = 0) 86 | { 87 | $url = '/gists/public'; 88 | return $this->connector->sendRequest($this->paginate($url, $page, $per_page))->body; 89 | } 90 | 91 | public function getStarred($page = 0, $per_page = 0) 92 | { 93 | $url = '/gists/starred'; 94 | return $this->connector->sendRequest($this->paginate($url, $page, $per_page))->body; 95 | } 96 | 97 | public function get($gist_id) 98 | { 99 | return $this->connector->sendRequest('/gists/'.(int)$gist_id)->body; 100 | } 101 | 102 | public function create($files, $public = false, $description = null) 103 | { 104 | $gist = new stdClass; 105 | $gist->public = $public; 106 | $gist->files = $files; 107 | 108 | if (!empty($description)) { 109 | $gist->description = $description; 110 | } 111 | 112 | return $this->connector->sendRequest('/gists', 'post', $gist)->body; 113 | } 114 | 115 | public function edit($gist_id, $files, $description = null) 116 | { 117 | $gist = new stdClass; 118 | $gist->files = $files; 119 | 120 | if (!empty($description)) { 121 | $gist->description = $description; 122 | } 123 | 124 | return $this->connector->sendRequest('/gists/'.(int)$gist_id, 'patch', $gist)->body; 125 | } 126 | 127 | public function star($gist_id) 128 | { 129 | return $this->connector->sendRequest('/gists/'.(int)$gist_id.'/star', 'put')->body; 130 | } 131 | 132 | public function unstar($gist_id) 133 | { 134 | return $this->connector->sendRequest('/gists/'.(int)$gist_id.'/star', 'delete')->body; 135 | } 136 | 137 | public function isStarred($gist_id) 138 | { 139 | $response = $this->connector->sendRequest('/gists/'.(int)$gist_id.'/star'); 140 | 141 | if ($response->code == '204') { 142 | return true; 143 | } else { // the code should be 404 144 | return false; 145 | } 146 | } 147 | 148 | public function fork($gist_id) 149 | { 150 | return $this->connector->sendRequest('/gists/'.(int)$gist_id.'/fork', 'put')->body; 151 | } 152 | 153 | public function delete($gist_id) 154 | { 155 | return $this->connector->sendRequest('/gists/'.(int)$gist_id, 'delete')->body; 156 | } 157 | 158 | public function getComments($gist_id) 159 | { 160 | return $this->connector->sendRequest('/gists/'.(int)$gist_id.'/comments')->body; 161 | } 162 | 163 | public function getComment($comment_id) 164 | { 165 | return $this->connector->sendRequest('/gists/comments/'.(int)$comment_id)->body; 166 | } 167 | 168 | public function createComment($gist_id, $comment) 169 | { 170 | return $this->connector->sendRequest('/gists/'.(int)$gist_id.'/comments', 'post', array('body' => $comment))->body; 171 | } 172 | 173 | public function editComment($comment_id, $comment) 174 | { 175 | return $this->connector->sendRequest('/gists/comments/'.(int)$comment_id, 'patch', array('body' => $comment))->body; 176 | } 177 | 178 | public function deleteComment($comment_id) 179 | { 180 | return $this->connector->sendRequest('/gists/comments/'.(int)$comment_id, 'delete')->body; 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /libraries/joomla/client/github/githubissues.php: -------------------------------------------------------------------------------- 1 | connector = $connector; 41 | } 42 | 43 | protected function paginate($url, $page = 0, $per_page = 0) 44 | { 45 | //TODO: Make a new base class and move paginate into it 46 | $query_string = array(); 47 | 48 | if ($page > 0) { 49 | $query_string[] = 'page='.(int)$page; 50 | } 51 | 52 | if ($per_page > 0) { 53 | $query_string[] = 'per_page='.(int)$per_page; 54 | } 55 | 56 | if (isset($query_string[0])) { 57 | $query = implode('&', $query_string); 58 | } else { 59 | $query = ''; 60 | } 61 | 62 | if (strlen($query) > 0) { 63 | if (strpos($url, '?') === false) { 64 | $url .= '?'.$query; 65 | } else { 66 | $url .= '&'.$query; 67 | } 68 | } 69 | 70 | return $url; 71 | } 72 | 73 | public function getAll($parameters = array(), $page = 0, $per_page = 0) 74 | { 75 | $url = '/issues'; 76 | 77 | $queryString = ''; 78 | 79 | foreach ($parameters AS $parameter) { 80 | $queryString .= ''; 81 | } 82 | if (isset($options['filter'])) { 83 | } 84 | return $this->connector->sendRequest($this->paginate($url, $page, $per_page))->body; 85 | } 86 | 87 | public function getByUser($user, $page = 0, $per_page = 0) 88 | { 89 | $url = '/users/'.$user.'/gists'; 90 | return $this->connector->sendRequest($this->paginate($url, $page, $per_page))->body; 91 | } 92 | 93 | public function getPublic($page = 0, $per_page = 0) 94 | { 95 | $url = '/gists/public'; 96 | return $this->connector->sendRequest($this->paginate($url, $page, $per_page))->body; 97 | } 98 | 99 | public function getStarred($page = 0, $per_page = 0) 100 | { 101 | $url = '/gists/starred'; 102 | return $this->connector->sendRequest($this->paginate($url, $page, $per_page))->body; 103 | } 104 | 105 | public function get($gist_id) 106 | { 107 | return $this->connector->sendRequest('/gists/'.(int)$gist_id)->body; 108 | } 109 | 110 | public function create($files, $public = false, $description = null) 111 | { 112 | $gist = new stdClass; 113 | $gist->public = $public; 114 | $gist->files = $files; 115 | 116 | if (!empty($description)) { 117 | $gist->description = $description; 118 | } 119 | 120 | return $this->connector->sendRequest('/gists', 'post', $gist)->body; 121 | } 122 | 123 | public function edit($gist_id, $files, $description = null) 124 | { 125 | $gist = new stdClass; 126 | $gist->files = $files; 127 | 128 | if (!empty($description)) { 129 | $gist->description = $description; 130 | } 131 | 132 | return $this->connector->sendRequest('/gists/'.(int)$gist_id, 'patch', $gist)->body; 133 | } 134 | 135 | public function star($gist_id) 136 | { 137 | return $this->connector->sendRequest('/gists/'.(int)$gist_id.'/star', 'put')->body; 138 | } 139 | 140 | public function unstar($gist_id) 141 | { 142 | return $this->connector->sendRequest('/gists/'.(int)$gist_id.'/star', 'delete')->body; 143 | } 144 | 145 | public function isStarred($gist_id) 146 | { 147 | $response = $this->connector->sendRequest('/gists/'.(int)$gist_id.'/star'); 148 | 149 | if ($response->code == '204') { 150 | return true; 151 | } else { // the code should be 404 152 | return false; 153 | } 154 | } 155 | 156 | public function fork($gist_id) 157 | { 158 | return $this->connector->sendRequest('/gists/'.(int)$gist_id.'/fork', 'put')->body; 159 | } 160 | 161 | public function delete($gist_id) 162 | { 163 | return $this->connector->sendRequest('/gists/'.(int)$gist_id, 'delete')->body; 164 | } 165 | 166 | public function getComments($gist_id) 167 | { 168 | return $this->connector->sendRequest('/gists/'.(int)$gist_id.'/comments')->body; 169 | } 170 | 171 | public function getComment($comment_id) 172 | { 173 | return $this->connector->sendRequest('/gists/comments/'.(int)$comment_id)->body; 174 | } 175 | 176 | public function createComment($gist_id, $comment) 177 | { 178 | return $this->connector->sendRequest('/gists/'.(int)$gist_id.'/comments', 'post', array('body' => $comment))->body; 179 | } 180 | 181 | public function editComment($comment_id, $comment) 182 | { 183 | return $this->connector->sendRequest('/gists/comments/'.(int)$comment_id, 'patch', array('body' => $comment))->body; 184 | } 185 | 186 | public function deleteComment($comment_id) 187 | { 188 | return $this->connector->sendRequest('/gists/comments/'.(int)$comment_id, 'delete')->body; 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /libraries/joomla/client/github/githubpulls.php: -------------------------------------------------------------------------------- 1 | connector->sendRequest($this->paginate($url, $page, $per_page))->body; 27 | } 28 | 29 | public function get($user, $repo, $pull_id) 30 | { 31 | return $this->connector->sendRequest('/repos/'.$user.'/'.$repo.'/pulls/'.(int)$pull_id)->body; 32 | } 33 | 34 | public function create($user, $repo, $title, $base, $head, $body = '') 35 | { 36 | $pull = new stdClass; 37 | $pull->title = $title; 38 | $pull->base = $base; 39 | $pull->head = $head; 40 | $pull->body = $body; 41 | 42 | return $this->connector->sendRequest('/repos/'.$user.'/'.$repo.'/pulls', 'post', $pull)->body; 43 | } 44 | 45 | public function createFromIssue($user, $repo, $issue, $base, $head) 46 | { 47 | $pull = new stdClass; 48 | $pull->issue = (int)$issue; 49 | $pull->base = $base; 50 | $pull->head = $head; 51 | 52 | return $this->connector->sendRequest('/repos/'.$user.'/'.$repo.'/pulls', 'post', $pull)->body; 53 | } 54 | 55 | public function edit($user, $repo, $id, $title = null, $body = null, $state = null) 56 | { 57 | $pull = new stdClass; 58 | 59 | if (isset($title)) { 60 | $pull->title = $title; 61 | } 62 | 63 | if (isset($body)) { 64 | $pull->body = $body; 65 | } 66 | 67 | if (isset($state)) { 68 | $pull->state = $state; 69 | } 70 | 71 | return $this->connector->sendRequest('/repos/'.$user.'/'.$repo.'/pulls/'.(int)$id, 'patch', $pull)->body; 72 | } 73 | 74 | public function getCommits($user, $repo, $pull_id, $page = 0, $per_page = 0) 75 | { 76 | $url = '/repos/'.$user.'/'.$repo.'/pulls/'.(int)$pull_id.'/commits'; 77 | return $this->connector->sendRequest($this->paginate($url, $page, $per_page))->body; 78 | } 79 | 80 | public function getFiles($user, $repo, $pull_id, $page = 0, $per_page = 0) 81 | { 82 | $url = '/repos/'.$user.'/'.$repo.'/pulls/'.(int)$pull_id.'/files'; 83 | return $this->connector->sendRequest($this->paginate($url, $page, $per_page))->body; 84 | } 85 | 86 | public function isMerged($user, $repo, $pull_id) 87 | { 88 | $url = '/repos/'.$user.'/'.$repo.'/pulls/'.(int)$pull_id.'/merge'; 89 | $response = $this->connector->sendRequest($url); 90 | 91 | if ($response->code == '204') { 92 | return true; 93 | } else { // the code should be 404 94 | return false; 95 | } 96 | } 97 | 98 | public function merge($user, $repo, $pull_id, $commit_message = '') 99 | { 100 | return $this->connector->sendRequest('/repos/'.$user.'/'.$repo.'/pulls/'.(int)$pull_id.'/merge', 'put', array('commit_message' => $commit_message))->body; 101 | } 102 | 103 | public function getComments($user, $repo, $pull_id, $page = 0, $per_page = 0) 104 | { 105 | $url = '/repos/'.$user.'/'.$repo.'/pulls/'.(int)$pull_id.'/comments'; 106 | return $this->connector->sendRequest($this->paginate($url, $page, $per_page))->body; 107 | } 108 | 109 | public function getComment($user, $repo, $comment_id) 110 | { 111 | return $this->connector->sendRequest('/repos/'.$user.'/'.$repo.'/pulls/comments/'.(int)$comment_id)->body; 112 | } 113 | 114 | public function createComment($user, $repo, $pull_id, $body, $commit_id, $path, $position) 115 | { 116 | $comment = new stdClass; 117 | $comment->body = $body; 118 | $comment->commit_id = $commit_id; 119 | $comment->path = $path; 120 | $comment->position = $position; 121 | 122 | return $this->connector->sendRequest('/repos/'.$user.'/'.$repo.'/pulls/'.(int)$pull_id.'/comments', 'post', $comment)->body; 123 | } 124 | 125 | public function createCommentReply($user, $repo, $pull_id, $body, $in_reply_to) 126 | { 127 | $comment = new stdClass; 128 | $comment->body = $body; 129 | $comment->in_reply_to = (int)$in_reply_to; 130 | 131 | return $this->connector->sendRequest('/repos/'.$user.'/'.$repo.'/pulls/'.(int)$pull_id.'/comments', 'post', $comment)->body; 132 | } 133 | 134 | public function editComment($user, $repo, $comment_id, $body) 135 | { 136 | return $this->connector->sendRequest('/repos/'.$user.'/'.$repo.'/pulls/comments/'.(int)$comment_id, 'patch', array('body' => $body))->body; 137 | } 138 | 139 | public function deleteComment($user, $repo, $comment_id) 140 | { 141 | return $this->connector->sendRequest('/repos/'.$user.'/'.$repo.'/pulls/comments/'.(int)$comment_id, 'delete')->body; 142 | } 143 | 144 | } 145 | -------------------------------------------------------------------------------- /libraries/joomla/client/githubobject.php: -------------------------------------------------------------------------------- 1 | connector = $connector; 41 | } 42 | 43 | protected function paginate($url, $page = 0, $per_page = 0) 44 | { 45 | //TODO: Make a new base class and move paginate into it 46 | $query_string = array(); 47 | 48 | if ($page > 0) { 49 | $query_string[] = 'page='.(int)$page; 50 | } 51 | 52 | if ($per_page > 0) { 53 | $query_string[] = 'per_page='.(int)$per_page; 54 | } 55 | 56 | if (isset($query_string[0])) { 57 | $query = implode('&', $query_string); 58 | } else { 59 | $query = ''; 60 | } 61 | 62 | if (strlen($query) > 0) { 63 | if (strpos($url, '?') === false) { 64 | $url .= '?'.$query; 65 | } else { 66 | $url .= '&'.$query; 67 | } 68 | } 69 | 70 | return $url; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /patchtester/build.sh: -------------------------------------------------------------------------------- 1 | rm -rf site 2 | rm -rf admin 3 | rm -rf ../patchtester.tar.bz2 4 | cp -r ../administrator/components/com_patchtester admin 5 | cp -r ../components/com_patchtester site 6 | rm -rf admin/backups/*.txt 7 | mv admin/patchtester.xml patchtester.xml 8 | tar jcf ../com_patchtester.tar.bz2 site admin patchtester.xml 9 | 10 | rm -rf github 11 | mkdir github 12 | cp ../libraries/joomla/client/github.php github 13 | cp ../libraries/joomla/client/githubobject.php github 14 | cp ../libraries/joomla/client/curl.php github 15 | cp -r ../libraries/joomla/client/github github 16 | cp github.xml github 17 | tar jcf ../file_github.tar.bz2 github/* 18 | 19 | tar jcf ../pkg_patchtester.tar.bz2 pkg_patchtester.xml ../com_patchtester.tar.bz2 ../file_github.tar.bz2 20 | -------------------------------------------------------------------------------- /patchtester/github.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Github Library 4 | Joomla! 5 | ianlenmac@gmail.com 6 | http://www.joomla.org 7 | (C) 2011 Open Source Matters. All Rights Reserved. 8 | GPL v2.0 or later 9 | 11.3 10 | October 2011 11 | FILES_TEST1_XML_DESCRIPTION 12 | 13 | 14 | 15 | 16 | curl.php 17 | github.php 18 | githubobject.php 19 | github 20 | 21 | 22 | 23 | 24 | 25 | https://raw.github.com/ianmacl/patchtester/master/updates/github.xml 26 | 27 | 28 | -------------------------------------------------------------------------------- /patchtester/pkg_patchtester.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Patch Tester 4 | patchtester 5 | 1.0alpha 6 | http://github.com/ianmacl/patchtester 7 | Ian MacLennan 8 | http://github.com/ianmacl/patchtester 9 | Patch Tester complete with Github Library 10 | 11 | file_github.tar.bz2 12 | com_patchtester.tar.bz2 13 | 14 | 15 | -------------------------------------------------------------------------------- /updates/github.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /updates/patchtester.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | --------------------------------------------------------------------------------