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/NbFileListField.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\Field\FilelistField;
use Joomla\CMS\Component\ComponentHelper;
use Joomla\CMS\Factory;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Uri\Uri;

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

/**
 * NbFileListField
 *
 * Field que lista arquivos de áudio e vídeo do servidor. Permite definir o diretório
 * diretamente no XML do field ou via parâmetro do componente, restringir extensões
 * permitidas e criar o diretório automaticamente quando necessário. Em caso de erro
 * exibe mensagens amigáveis abaixo do campo.
 * 
 * Utilizado inicialmente em: No Boss FAQ e No Boss Video Gallery.
 *
 * Documentacao: https://wiki.nobossextensions.com/private/nb/plataforma-e-extensoes/no-boss-file-list-field
 */
class NbFileListField extends FilelistField {

	protected $type = "NbFileList";

    /**
     * Mensagem de erro ao tentar criar o diretório (exibida abaixo do campo)
     * @var string
     */
    protected $directoryCreationError = '';

    protected function getOptions() {
        $options = parent::getOptions();
        array_unshift($options, HTMLHelper::_('select.option', '', Text::_('LIB_NOBOSS_FIELD_NBFILELIST_SELECT_FILE')));
        return $options;
    }

    public function setup(\SimpleXMLElement $element, $value, $group = null){       
        // Obtém o componente que estamos editando dinamicamente
        $input = Factory::getApplication()->input;
        $option = $input->getCmd('option', '');

        $componentParams = null;
        if(!empty($option)) {
            $componentParams = ComponentHelper::getParams($option);
        }

        // Armazena extensoes de arquivos permitidas
        $extList = '';

        // Extensoes de arquivos permitidos - se o field XML indicar um nome de parâmetro do componente (config_field_name_file_granted) e o parâmetro estiver definido no componente, usamos esse valor. Caso contrário, usamos o valor definido diretamente no XML do field (file_upload_extensions_granted) se presente.
        if (isset($element['config_field_name_file_granted']) && (string) $element['config_field_name_file_granted'] !== '' && $componentParams) {
            $paramNameFileGranted = (string) $element['config_field_name_file_granted'];
            $paramValueFileGranted = $componentParams->get($paramNameFileGranted, '');
            if ($paramValueFileGranted !== '') {
                // If the param is a multiple select (list with multiple), it may return an array.
                // Convert array to CSV so later processing (explode) works as expected.
                if (is_array($paramValueFileGranted)) {
                    $extList = implode(',', $paramValueFileGranted);
                } else {
                    $extList = (string) $paramValueFileGranted;
                }
            }
        }

        // Extensoes de arquivos permitidos - Se não obtemos a lista via config do componente, usa o valor direto no XML do field
        if ($extList === '' && isset($element['file_upload_extensions_granted']) && (string) $element['file_upload_extensions_granted'] !== '') {
            $extList = (string) $element['file_upload_extensions_granted'];
        }

        // Extensoes de arquivos permitidos - Se agora temos uma lista de extensões, converte para regex e define no elemento
        if ($extList !== '') {
            // Normaliza: divide por vírgula, trim, remove pontos iniciais e itens vazios
            $parts = array_filter(array_map(function ($p) {
                $p = trim($p);
                $p = ltrim($p, '.');
                return $p === '' ? null : $p;
            }, explode(',', $extList)));

            if (!empty($parts)) {
                // Escapa cada parte para uso em regex e constrói um padrão que case as extensões (case-insensitive)
                $escaped = array_map(function ($p) {
                    return preg_quote($p, '/');
                }, $parts);

                $pattern = '(?i)\.(' . implode('|', $escaped) . ')$';

                // Define ambos os nomes de atributo possíveis para que o FilelistField os reconheça
                $element['filter'] = $pattern;
                $element['fileFilter'] = $pattern;
            }
        }

        // Inicializa variável que conterá o diretório a ser listado
        $directoryList = '';

        // Diretório a ser listado - se o field XML indicar um nome de parâmetro do componente (config_field_name_directory) e o parâmetro estiver definido no componente, usamos esse valor. Caso contrário, usamos o valor definido diretamente no XML do field (directory) se presente.
        if (isset($element['config_field_name_directory']) && (string) $element['config_field_name_directory'] !== '' && $componentParams) {
            $paramNameDirectory = (string) $element['config_field_name_directory'];
            $paramValueDirectory = $componentParams->get($paramNameDirectory, '');
            if ($paramValueDirectory !== '') {
                $directoryList = (string) $paramValueDirectory;
            }
        }

        // Diretório a ser listado - Se não obtemos o diretório via config do componente, usa o valor direto no XML do field
        if ($directoryList === '' && isset($element['directory']) && (string) $element['directory'] !== '') {
            $directoryList = (string) $element['directory'];
        }

        // Diretório a ser listado - Se ainda não temos diretório, informa erro
        if ($directoryList === '') {
            $this->directoryCreationError = '<div class="nbfilelist__error">' .
                'No directory was provided in the field (attribute "directory" or "config_field_name_directory" is missing).' .
                '</div>';
        }

        // Diretório a ser listado - Se temos um valor em $directoryList, realiza verificação/ criação do diretório
        if ($directoryList !== '') {
            // Sanitiza: remove bytes nulos e espaços
            $directoryList = trim(str_replace("\0", '', $directoryList));

            // Rejeita caminhos com segmentos de subida (..)
            if (strpos($directoryList, '..') !== false) {
                $this->directoryCreationError = '<div class="nbfilelist__error">' .
                    'The provided path contains invalid segments (..).' .
                    '</div>';
                $element['directory'] = '';
                return parent::setup($element, $value, $group);
            }

            // Substitui %s no label pelo diretório, se presente
            if (isset($element['label']) && strpos(Text::_($element['label']), '%s') !== false) {
                $element['label'] = sprintf(Text::_($element['label']), $directoryList);
            }

            // Substitui %s no description pelo diretório, se presente
            if (isset($element['description']) && strpos(Text::_($element['description']), '%s') !== false) {
                $element['description'] = sprintf(Text::_($element['description']), $directoryList);
            }

            // Monta caminho completo com JPATH_ROOT
            $trimmed = ltrim($directoryList, DIRECTORY_SEPARATOR);
            $fullPath = rtrim(JPATH_ROOT, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $trimmed;

            // Se o diretório não existir, tenta criar (recursivo)
            if (!is_dir($fullPath)) {
                // Tenta criar e captura eventual erro
                $created = @mkdir($fullPath, 0755, true);

                if (!$created) {
                    $lastError = error_get_last();
                    $message = isset($lastError['message']) ? $lastError['message'] : 'Error creating directory.';


                    // Armazena a mensagem para ser exibida no getInput()
                    $this->directoryCreationError = '<div class="nbfilelist__error">' .
                        'Error creating directory <strong>' . htmlspecialchars($fullPath, ENT_QUOTES, 'UTF-8') . '</strong>: ' .
                        htmlspecialchars($message, ENT_QUOTES, 'UTF-8') .
                        '</div>';
                    $element['directory'] = '';
                }
            } else {
                // Diretório existe — verifica se é legível. Se não for legível, evita que o core do Joomla tente listar e gere erro global.
                if (!is_readable($fullPath)) {
                    $this->directoryCreationError = '<div class="nbfilelist__error">' .
                        'The directory <strong>' . htmlspecialchars($fullPath, ENT_QUOTES, 'UTF-8') . '</strong> exists but is not readable by the webserver. Please check permissions.' .
                        '</div>';
                    $element['directory'] = '';
                }
            }
        }

        return parent::setup($element, $value, $group);
    }

    protected function getInput(){
        // Obtém o document de forma robusta e pega o WebAssetManager quando disponível
        $doc = Factory::getApplication()->getDocument();
        $wa = (is_object($doc) && method_exists($doc, 'getWebAssetManager')) ? $doc->getWebAssetManager() : null;

        // JS e CSS do field — registra o estilo apenas se o WebAssetManager estiver disponível
        if ($wa) {
            // $wa->registerAndUseScript('nobossfilelist', Uri::root()."libraries/noboss/src/Form/Field/assets/js/min/nobossfilelist.min.js");
            $wa->registerAndUseStyle('nobossfilelist', Uri::root()."libraries/noboss/src/Form/Field/assets/stylesheets/css/nobossfilelist.min.css");
        }

        $html = parent::getInput();

        // Botão para abrir o Gerenciador de Mídias em nova aba
        $mediaUrl = 'index.php?option=com_media&path=local-files:/';
        $label = Text::_('LIB_NOBOSS_FIELD_NBFILELIST_OPEN_MEDIA_MANAGER');
        $button = '<a href="' . htmlspecialchars($mediaUrl, ENT_QUOTES, 'UTF-8') . '" '
            . 'target="_blank" rel="noopener" '
            . 'class="btn btn-secondary btn-sm nbfilelist__media-btn" '
            . 'style="display:inline-block; margin-top:6px">' . htmlspecialchars($label, ENT_QUOTES, 'UTF-8') . '</a>';

        // Botão para abrir a documentação externa (wiki)
        $docUrl = 'https://wiki.nobossextensions.com/no-boss-library/como-subir-um-arquivo-audio-video-para-o-servidor-do-seu-site';
        $docLabel = Text::_('LIB_NOBOSS_FIELD_NBFILELIST_OPEN_DOC');
        // Exibe a documentação como um link simples (visual de link, não botão)
        $docButton = '<a href="' . htmlspecialchars($docUrl, ENT_QUOTES, 'UTF-8') . '" '
            . 'target="_blank" rel="noopener" '
            . 'class="nbfilelist__doc-link">' . htmlspecialchars($docLabel, ENT_QUOTES, 'UTF-8') . '</a>';

        // Insere os botões logo após o input do field (mídias e documentação)
        $html .= $button . $docButton;

        // Se houve erro ao criar diretório, exibe mensagem em vermelho abaixo do campo
        if (!empty($this->directoryCreationError)) {
            $html .= $this->directoryCreationError;
        }

        return $html;
    }
}