├── 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 |
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 |
21 |
22 |
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 |
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 |
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 |
--------------------------------------------------------------------------------