93f0fa0a47
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 9s
- SshKeyField: detect base64-encoded keys from DB so the "Key loaded" badge displays correctly after initial upload - Add COM_MOKOJOOMBACKUP_BACKUPS_N_ITEMS_DELETED language keys for Joomla's AdminController delete feedback message
113 lines
3.7 KiB
PHP
113 lines
3.7 KiB
PHP
<?php
|
|
|
|
/**
|
|
* @package MokoSuiteBackup
|
|
* @subpackage com_mokosuitebackup
|
|
* @author Moko Consulting <hello@mokoconsulting.tech>
|
|
* @copyright Copyright (C) 2026 Moko Consulting. All rights reserved.
|
|
* @license GNU General Public License version 3 or later; see LICENSE
|
|
*
|
|
* Custom field for SSH private key input.
|
|
* Supports both file upload (via FileReader JS) and paste-in textarea.
|
|
* The key content is stored in the database, not as a file on disk.
|
|
*/
|
|
|
|
namespace Joomla\Component\MokoSuiteBackup\Administrator\Field;
|
|
|
|
defined('_JEXEC') or die;
|
|
|
|
use Joomla\CMS\Form\FormField;
|
|
use Joomla\CMS\Language\Text;
|
|
|
|
class SshKeyField extends FormField
|
|
{
|
|
protected $type = 'SshKey';
|
|
|
|
protected function getInput(): string
|
|
{
|
|
$value = $this->value ?? '';
|
|
$id = $this->id;
|
|
$name = $this->name;
|
|
|
|
$decoded = !empty($value) ? (base64_decode($value, true) ?: '') : '';
|
|
$hasKey = !empty($value) && ($value === '__KEEP_EXISTING__'
|
|
|| str_contains($value, 'PRIVATE KEY')
|
|
|| str_contains($decoded, 'PRIVATE KEY'));
|
|
|
|
$html = '<div id="' . htmlspecialchars($id) . '-wrapper">';
|
|
|
|
/* Status badge */
|
|
if ($hasKey) {
|
|
$html .= '<span class="badge bg-success me-2">'
|
|
. '<span class="icon-lock" aria-hidden="true"></span> '
|
|
. Text::_('COM_MOKOJOOMBACKUP_FIELD_SFTP_KEY_LOADED')
|
|
. '</span>';
|
|
}
|
|
|
|
/* File upload button */
|
|
$html .= '<label class="btn btn-outline-secondary btn-sm" for="' . htmlspecialchars($id) . '-file">';
|
|
$html .= '<span class="icon-upload" aria-hidden="true"></span> ';
|
|
$html .= $hasKey ? Text::_('COM_MOKOJOOMBACKUP_FIELD_SFTP_KEY_REPLACE') : Text::_('COM_MOKOJOOMBACKUP_FIELD_SFTP_KEY_UPLOAD');
|
|
$html .= '</label>';
|
|
$html .= '<input type="file" id="' . htmlspecialchars($id) . '-file"'
|
|
. ' accept=".pem,.key,.openssh,.ppk,*" style="display:none;"'
|
|
. ' onchange="mokoSshKeyFileSelected(\'' . htmlspecialchars($id) . '\', this)">';
|
|
|
|
$html .= '<span id="' . htmlspecialchars($id) . '-status" class="ms-2 text-muted small"></span>';
|
|
|
|
if ($hasKey) {
|
|
$html .= ' <button type="button" class="btn btn-sm btn-outline-danger ms-2"'
|
|
. ' onclick="mokoSshKeyClear(\'' . htmlspecialchars($id) . '\')">'
|
|
. '<span class="icon-times" aria-hidden="true"></span> '
|
|
. Text::_('COM_MOKOJOOMBACKUP_FIELD_SFTP_KEY_CLEAR')
|
|
. '</button>';
|
|
}
|
|
|
|
/* Hidden field — key data is NEVER rendered as visible text.
|
|
On existing keys, we submit a sentinel value to preserve the DB value
|
|
unless a new file is uploaded or clear is clicked. */
|
|
if ($hasKey) {
|
|
$html .= '<input type="hidden" name="' . htmlspecialchars($name) . '" id="' . htmlspecialchars($id) . '"'
|
|
. ' value="__KEEP_EXISTING__">';
|
|
} else {
|
|
$html .= '<input type="hidden" name="' . htmlspecialchars($name) . '" id="' . htmlspecialchars($id) . '"'
|
|
. ' value="">';
|
|
}
|
|
|
|
$html .= '</div>';
|
|
$html .= $this->getScript();
|
|
|
|
return $html;
|
|
}
|
|
|
|
private function getScript(): string
|
|
{
|
|
return <<<'JS'
|
|
<script>
|
|
function mokoSshKeyFileSelected(fieldId, input) {
|
|
if (!input.files || !input.files[0]) return;
|
|
var file = input.files[0];
|
|
var reader = new FileReader();
|
|
reader.onload = function(e) {
|
|
/* Base64 encode the key before storing in the hidden field */
|
|
var content = e.target.result;
|
|
var encoded = btoa(content);
|
|
document.getElementById(fieldId).value = encoded;
|
|
var status = document.getElementById(fieldId + '-status');
|
|
if (status) status.textContent = file.name + ' uploaded';
|
|
};
|
|
reader.readAsText(file);
|
|
}
|
|
|
|
function mokoSshKeyClear(fieldId) {
|
|
document.getElementById(fieldId).value = '';
|
|
var status = document.getElementById(fieldId + '-status');
|
|
if (status) status.textContent = 'Key removed';
|
|
var fileInput = document.getElementById(fieldId + '-file');
|
|
if (fileInput) fileInput.value = '';
|
|
}
|
|
</script>
|
|
JS;
|
|
}
|
|
}
|