Your IP : 216.73.216.224


Current Path : /var/www/html/libraries/noboss/src/Form/Field/
Upload File :
Current File : /var/www/html/libraries/noboss/src/Form/Field/NbTransferToolsField.php

<?php
/**
 * @package         No Boss Extensions
 * @subpackage      No Boss Library
 * @author          No Boss Technology <contact@nobosstechnology.com>
 * @copyright       Copyright (C) 2026 No Boss Technology. All rights reserved.
 * @license         GNU Lesser General Public License version 3 or later; see <https://www.gnu.org/licenses/lgpl-3.0.en.html>
 */

namespace Noboss\Library\Form\Field;

use Joomla\CMS\Form\FormField;
use Joomla\CMS\Factory;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Uri\Uri;

// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects

/**
 * Field de ferramentas para exportar e importar os parâmetros (params) do módulo em formato JSON.
 *
 * @since  1.0.0
 */
class NbTransferToolsField extends FormField
{
    /**
     * Tipo do field no XML do formulário.
     *
     * @var    string
     * @since  1.0.0
     */
    protected $type = 'NbTransferTools';

    /**
     * Omite a label padrão do Joomla — o título é renderizado dentro do markup do field.
     *
     * @return  string
     *
     * @since   1.0.0
     */
    protected function getLabel(): string
    {
        return '';
    }

    /**
     * Renderiza a seção de copiar configurações com botões de exportar/importar params do módulo.
     *
     * Botões ficam desabilitados no site demo ou quando o módulo ainda não foi salvo (sem ID).
     *
     * @return  string  HTML da seção de ferramentas
     *
     * @since   1.0.0
     */
    protected function getInput(): string
    {
        $editModuleId    = $this->getEditModuleId();
        $moduleIdForDom  = $editModuleId > 0 ? $editModuleId : 0;
        $buttonsDisabled = $this->isSiteDemo() || $editModuleId === 0;

        $exportUrl   = $this->buildNobossAjaxUrl('noboss.src.Util.NbModuleParamsUtil', 'exportModuleParams', ['id_module' => max($editModuleId, 1)]);
        $importUrl   = $this->buildNobossAjaxUrl('noboss.src.Util.NbModuleParamsUtil', 'importModuleParams');
        $inputFileId = 'nb-layout-import-file-' . $moduleIdForDom;
        $statusId    = 'nb-layout-import-status-' . $moduleIdForDom;

        if ($buttonsDisabled) {
            $exportButton = $this->renderDisabledButton('icon-download', 'LIB_NOBOSS_FIELD_NBTRANSFERTOOLS_EXPORT_BUTTON');
            $importButton = $this->renderDisabledButton('icon-upload', 'LIB_NOBOSS_FIELD_NBTRANSFERTOOLS_IMPORT_BUTTON');
        } else {
            $exportButton = $this->renderLinkButton($exportUrl, 'icon-download', 'LIB_NOBOSS_FIELD_NBTRANSFERTOOLS_EXPORT_BUTTON');

            $importButton  = '<input type="file" id="' . $inputFileId . '" accept=".json,application/json" style="display:none;" />';
            $importButton .= '<label for="' . $inputFileId . '" class="btn btn-secondary hasTooltip" style="cursor:pointer; margin-bottom:0;">';
            $importButton .= '<span class="icon-upload" aria-hidden="true"></span> ';
            $importButton .= '<span>' . Text::_('LIB_NOBOSS_FIELD_NBTRANSFERTOOLS_IMPORT_BUTTON') . '</span>';
            $importButton .= '</label>';
        }

        $statusDiv = '<div id="' . $statusId . '" role="alert" aria-live="polite" style="display:none; margin-top: 0.75rem;"></div>';

        Factory::getApplication()->getDocument()->getWebAssetManager()->addInline(
            'style',
            '.nb-tools-section .btn::before{display:none!important;content:none!important}',
            ['name' => 'nb-tools-section-btn-icons']
        );

        $html  = '<div class="nb-transfer-tools nb-tools-section" style="margin-top: 20px;">';
        $html .= '<h4><span class="icon-copy" aria-hidden="true"></span> ' . Text::_('LIB_NOBOSS_FIELD_NBTRANSFERTOOLS_TITLE') . '</h4>';
        $html .= '<hr>';
        $html .= '<p class="mb-2 small text-muted"><span class="icon-info-circle" aria-hidden="true"></span> '
            . Text::_('LIB_NOBOSS_FIELD_NBTRANSFERTOOLS_INFO_NOTE') . '</p>';
        $html .= '<div class="btn-toolbar" role="toolbar" style="gap: 0.5rem; flex-wrap: wrap;">' . $exportButton . $importButton . '</div>';
        $html .= $statusDiv;
        $html .= '</div>';

        if (!$buttonsDisabled) {
            Factory::getApplication()->getDocument()->getWebAssetManager()->addInline(
                'script',
                $this->buildImportScript($inputFileId, $statusId, $importUrl, $editModuleId),
                ['name' => 'nb-layout-import-' . $editModuleId]
            );
        }

        return $html;
    }

    /**
     * Verifica se a extensão está sendo executada no site de demonstração No Boss.
     *
     * @return  bool
     *
     * @since   1.0.0
     */
    private function isSiteDemo(): bool
    {
        return strpos(Uri::root(), 'nobossextensions.com/demos') !== false;
    }

    /**
     * Monta URL para requisição AJAX via com_nobossajax.
     *
     * @param   string  $library  Caminho da library (ex.: noboss.src.Util.NbModuleParamsUtil)
     * @param   string  $method   Método a invocar
     * @param   array   $params   Parâmetros adicionais da query string
     *
     * @return  string
     *
     * @since   1.0.0
     */
    private function buildNobossAjaxUrl(string $library, string $method, array $params = []): string
    {
        $query = http_build_query(array_merge([
            'option'  => 'com_nobossajax',
            'library' => $library,
            'method'  => $method,
            'format'  => 'raw',
        ], $params));

        return Uri::root() . 'index.php?' . $query;
    }

    /**
     * Retorna o ID do módulo em edição no com_modules, ou 0 quando não aplicável.
     *
     * @return  int
     *
     * @since   1.0.0
     */
    private function getEditModuleId(): int
    {
        $input = Factory::getApplication()->input;

        if ($input->getCmd('option') === 'com_modules') {
            return $input->getInt('id', 0);
        }

        return 0;
    }

    /**
     * Renderiza um link estilizado como botão secundário do Joomla.
     *
     * @param   string  $url       URL completa do href
     * @param   string  $icon      Classe do ícone Joomla
     * @param   string  $labelKey  Chave de tradução do rótulo e tooltip
     *
     * @return  string
     *
     * @since   1.0.0
     */
    private function renderLinkButton(string $url, string $icon, string $labelKey): string
    {
        $label = Text::_($labelKey);

        return '<a href="' . $url . '" target="_blank" rel="noopener noreferrer" class="btn btn-secondary hasTooltip" title="' . $label . '">'
             . '<span class="' . $icon . '" aria-hidden="true"></span> '
             . '<span>' . $label . '</span>'
             . '</a>';
    }

    /**
     * Renderiza um botão visualmente idêntico ao ativo, porém sem ação (estado disabled).
     *
     * @param   string  $icon      Classe do ícone Joomla
     * @param   string  $labelKey  Chave de tradução do rótulo e tooltip
     *
     * @return  string
     *
     * @since   1.0.0
     */
    private function renderDisabledButton(string $icon, string $labelKey): string
    {
        $label = Text::_($labelKey);

        return '<span class="btn btn-secondary disabled" aria-disabled="true" title="' . $label . '">'
             . '<span class="' . $icon . '" aria-hidden="true"></span> '
             . '<span>' . $label . '</span>'
             . '</span>';
    }

    /**
     * Gera o JavaScript do fluxo de importação de params via upload de arquivo JSON.
     *
     * @param   string  $inputFileId   ID do input file oculto
     * @param   string  $statusId      ID do elemento de feedback de status
     * @param   string  $importUrl     URL do endpoint de importação
     * @param   int     $editModuleId  ID do módulo enviado no POST
     *
     * @return  string
     *
     * @since   1.0.0
     */
    private function buildImportScript(string $inputFileId, string $statusId, string $importUrl, int $editModuleId): string
    {
        $importUrl    = addslashes($importUrl);
        $editModuleId = (int) $editModuleId;

        $msgProcessing  = addslashes(Text::_('LIB_NOBOSS_FIELD_NBTRANSFERTOOLS_IMPORT_PROCESSING'));
        $msgInvalidJson = addslashes(Text::_('LIB_NOBOSS_FIELD_NBTRANSFERTOOLS_IMPORT_ERROR_INVALID_JSON'));
        $msgInvalidObj  = addslashes(Text::_('LIB_NOBOSS_FIELD_NBTRANSFERTOOLS_IMPORT_ERROR_INVALID_STRUCTURE'));
        $msgNetworkErr  = addslashes(Text::_('LIB_NOBOSS_FIELD_NBTRANSFERTOOLS_IMPORT_ERROR_NETWORK'));
        $msgConfirm     = addslashes(Text::_('LIB_NOBOSS_FIELD_NBTRANSFERTOOLS_IMPORT_CONFIRM'));

        return <<<JS
document.addEventListener('DOMContentLoaded', function () {
    var fileInput = document.getElementById('{$inputFileId}');
    var statusEl  = document.getElementById('{$statusId}');

    if (!fileInput || !statusEl) { return; }

    function showStatus(message, type) {
        statusEl.className     = 'alert alert-' + type;
        statusEl.innerHTML     = message;
        statusEl.style.display = 'block';
    }

    fileInput.addEventListener('change', function () {
        var file = fileInput.files[0];
        if (!file) { return; }

        statusEl.style.display = 'none';
        fileInput.value = '';

        var reader = new FileReader();
        reader.onload = function (e) {
            var raw = e.target.result;

            var parsed;
            try {
                parsed = JSON.parse(raw);
            } catch (err) {
                showStatus('<span class="icon-warning" aria-hidden="true"></span> {$msgInvalidJson}', 'warning');
                return;
            }

            if (typeof parsed !== 'object' || Array.isArray(parsed) || parsed === null) {
                showStatus('<span class="icon-warning" aria-hidden="true"></span> {$msgInvalidObj}', 'warning');
                return;
            }

            if (!confirm('{$msgConfirm}')) { return; }

            showStatus('<span class="icon-spinner icon-spin" aria-hidden="true"></span> {$msgProcessing}', 'info');

            var formData = new FormData();
            formData.append('id_module',    '{$editModuleId}');
            formData.append('json_content', raw);

            fetch('{$importUrl}', {
                method:      'POST',
                credentials: 'same-origin',
                body:        formData
            })
            .then(function (response) { return response.json(); })
            .then(function (data) {
                if (data && data.success) {
                    showStatus('<span class="icon-checkmark" aria-hidden="true"></span> ' + data.message, 'success');
                } else {
                    showStatus('<span class="icon-warning" aria-hidden="true"></span> ' + (data ? data.message : '{$msgNetworkErr}'), 'warning');
                }
            })
            .catch(function () {
                showStatus('<span class="icon-warning" aria-hidden="true"></span> {$msgNetworkErr}', 'warning');
            });
        };

        reader.readAsText(file, 'UTF-8');
    });
});
JS;
    }
}