Skip to content

Commit 0b1c1d3

Browse files
authored
Merge pull request #287 from iMattPro/db-con-test
Add database connection test
2 parents 37c84a7 + 96f82ea commit 0b1c1d3

4 files changed

Lines changed: 143 additions & 1 deletion

File tree

language/en/qi.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,4 +311,14 @@
311311
'YES' => 'Yes',
312312

313313
'COLON' => ':',
314+
315+
// Database connection test
316+
'DB_TEST_TYPE_REQUIRED' => 'Database type is required',
317+
'DB_TEST_HOST_REQUIRED' => 'Database host is required',
318+
'DB_TEST_CONNECTION_SUCCESS' => 'Database connection successful',
319+
'DB_TEST_CONNECTION_FAILED' => 'Connection failed',
320+
'DB_TEST_SQLITE3_AVAILABLE' => 'SQLite3 extension is available',
321+
'DB_TEST_SQLITE_AVAILABLE' => 'SQLite extension is available',
322+
'DB_TEST_SQLITE_NOT_AVAILABLE' => 'SQLite extension not available',
323+
'TEST_DATABASE_CONNECTION' => 'Test Database Connection',
314324
));

modules/qi_settings.php

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,13 @@ public function run()
1616
$saved = false;
1717
$config_text = '';
1818
$errors = [];
19-
if ($mode === 'update_settings')
19+
20+
if ($mode === 'test_db_connection')
21+
{
22+
$this->test_db_connection();
23+
return;
24+
}
25+
else if ($mode === 'update_settings')
2026
{
2127
$qi_config = @utf8_normalize_nfc(qi_request_var('qi_config', array('' => ''), true));
2228

@@ -147,4 +153,79 @@ public function run()
147153

148154
qi::page_display('settings_body');
149155
}
156+
157+
private function test_db_connection()
158+
{
159+
header('Content-Type: application/json');
160+
161+
$dbms = qi_request_var('dbms', '');
162+
$dbhost = qi_request_var('dbhost', '');
163+
$dbport = qi_request_var('dbport', '');
164+
$dbuser = qi_request_var('dbuser', '');
165+
$dbpasswd = qi_request_var('dbpasswd', '');
166+
167+
if (empty($dbms))
168+
{
169+
echo json_encode(['success' => false, 'message' => qi::lang('DB_TEST_TYPE_REQUIRED')]);
170+
return;
171+
}
172+
173+
// SQLite doesn't use server connections, just test if extension is available
174+
if (in_array($dbms, ['sqlite', 'sqlite3']))
175+
{
176+
try
177+
{
178+
if ($dbms === 'sqlite3')
179+
{
180+
new \SQLite3(':memory:');
181+
echo json_encode(['success' => true, 'message' => qi::lang('DB_TEST_SQLITE3_AVAILABLE')]);
182+
}
183+
else
184+
{
185+
$error = null;
186+
@sqlite_open(':memory:', 0666, $error);
187+
echo json_encode(['success' => true, 'message' => qi::lang('DB_TEST_SQLITE_AVAILABLE')]);
188+
}
189+
}
190+
catch (Exception $e)
191+
{
192+
echo json_encode(['success' => false, 'message' => qi::lang('DB_TEST_SQLITE_NOT_AVAILABLE')]);
193+
}
194+
return;
195+
}
196+
197+
if (empty($dbhost))
198+
{
199+
echo json_encode(['success' => false, 'message' => qi::lang('DB_TEST_HOST_REQUIRED')]);
200+
return;
201+
}
202+
203+
// we need to capture trigger_error() calls to be able to continue
204+
set_error_handler(function() {
205+
return true;
206+
});
207+
208+
try
209+
{
210+
$db_data = [$dbms, $dbhost, $dbuser, $dbpasswd, $dbport, ''];
211+
$db = db_connect($db_data);
212+
213+
if ($db && $db->db_connect_id)
214+
{
215+
$db->sql_close();
216+
restore_error_handler();
217+
echo json_encode(['success' => true, 'message' => qi::lang('DB_TEST_CONNECTION_SUCCESS')]);
218+
}
219+
else
220+
{
221+
restore_error_handler();
222+
echo json_encode(['success' => false, 'message' => qi::lang('DB_TEST_CONNECTION_FAILED')]);
223+
}
224+
}
225+
catch (Exception $e)
226+
{
227+
restore_error_handler();
228+
echo json_encode(['success' => false, 'message' => qi::lang('DB_TEST_CONNECTION_FAILED')]);
229+
}
230+
}
150231
}

style/assets/js/scripts.js

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,48 @@
162162
});
163163
}
164164

165+
// Database connection test
166+
const $testDbBtn = $('#test-db-connection');
167+
if ($testDbBtn) {
168+
$testDbBtn.addEventListener('click', () => {
169+
const $result = $('#db-test-result');
170+
$testDbBtn.disabled = true;
171+
$testDbBtn.innerHTML = '<span class="spinner-border spinner-border-sm" role="status"></span> Testing...';
172+
173+
const formData = new FormData();
174+
formData.append('dbms', $('#dbms').value);
175+
formData.append('dbhost', $('#dbhost').value);
176+
formData.append('dbport', $('#dbport').value);
177+
formData.append('dbuser', $('#dbuser').value);
178+
formData.append('dbpasswd', $('#dbpasswd').value);
179+
180+
const xhr = new XMLHttpRequest();
181+
xhr.responseType = 'json';
182+
xhr.addEventListener('loadend', () => {
183+
$testDbBtn.disabled = false;
184+
$testDbBtn.innerHTML = '<svg class="bi" width="16" height="16" fill="currentColor"><use xlink:href="style/assets/img/bootstrap-icons.svg#database-check"/></svg> Test Database Connection';
185+
186+
if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {
187+
const response = xhr.response;
188+
if (response.success) {
189+
$result.className = 'mt-2 alert alert-success';
190+
$result.innerHTML = '<svg class="bi text-success" width="16" height="16" fill="currentColor"><use xlink:href="style/assets/img/bootstrap-icons.svg#check-circle-fill"/></svg> ' + response.message;
191+
} else {
192+
$result.className = 'mt-2 alert alert-danger';
193+
$result.innerHTML = '<svg class="bi text-danger" width="16" height="16" fill="currentColor"><use xlink:href="style/assets/img/bootstrap-icons.svg#exclamation-triangle-fill"/></svg> ' + response.message;
194+
}
195+
} else {
196+
$result.className = 'mt-2 alert alert-danger';
197+
$result.innerHTML = '<svg class="bi text-danger" width="16" height="16" fill="currentColor"><use xlink:href="style/assets/img/bootstrap-icons.svg#exclamation-triangle-fill"/></svg> Connection test failed';
198+
}
199+
});
200+
201+
xhr.open('POST', 'index.php?page=settings&mode=test_db_connection');
202+
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
203+
xhr.send(formData);
204+
});
205+
}
206+
165207
// Notification of QI update (use sessionStorage for dismissed notification)
166208
if (sessionStorage.getItem('qiupdate') === null) {
167209
const qiUpdateToast = $('#qiUpdateToast');

style/settings_body.twig

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,15 @@
176176
<label for="table_prefix" class="col-md-5 form-label">{{ lang('TABLE_PREFIX') ~ lang('COLON') }}<br><span class="form-text text-muted">{{ lang('TABLE_PREFIX_EXPLAIN') }} {{ lang('THIS_CAN_CHANGE') }}</span></label>
177177
<div class="col-md-7"><input class="form-control" type="text" id="table_prefix" name="qi_config[table_prefix]" value="{{ CONFIG_TABLE_PREFIX }}" placeholder="{{ lang('REQUIRED') }}"></div>
178178
</div>
179+
<div class="mb-3 row">
180+
<div class="col-md-7 offset-md-5">
181+
<button type="button" id="test-db-connection" class="btn btn-outline-primary">
182+
<svg class="bi" width="16" height="16" fill="currentColor"><use xlink:href="{{ QI_ROOT_PATH }}style/assets/img/bootstrap-icons.svg#database-check"/></svg>
183+
{{ lang('TEST_DATABASE_CONNECTION') }}
184+
</button>
185+
<div id="db-test-result" class="mt-2 d-none"></div>
186+
</div>
187+
</div>
179188
</fieldset>
180189
</div>
181190
</div>

0 commit comments

Comments
 (0)