| Current Path : /var/www/html/libraries/noboss/src/Form/Field/Nbtheme/ |
| Current File : /var/www/html/libraries/noboss/src/Form/Field/Nbtheme/NbthemeHelper.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\Nbtheme;
use Joomla\CMS\Factory;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Form\FormFactoryInterface;
use Noboss\Library\Util\NbCurlUtil;
use Noboss\Library\Util\NbUrlUtil;
use Joomla\CMS\Uri\Uri;
use Joomla\CMS\Language\LanguageFactoryInterface;
use Joomla\Filesystem\Path;
defined('_JEXEC') or die;
/**
* Classe de campo personalizado para exibição de temas (executado a partir de JS)
*/
class NbthemeHelper {
/**
* Proxy server-side para a listagem de exemplos de temas hospedada em No Boss Extensions.
*
* Encapsula a chamada HTTP cross-origin que antes era feita direto pelo navegador
* (`nobossextensions.com/index.php?option=com_nbextensoes&task=externalthemes.getThemesSamples`),
* permitindo que o JS da modal de temas execute o XHR contra a propria origem
* e portanto seja compativel com sites que aplicam Content Security Policy
* com `connect-src 'self'`.
*
* Acessivel via:
* WEBSITE/index.php?option=com_nobossajax&library=noboss.src.Form.Field.Nbtheme.NbthemeHelper&method=getThemesSamples&format=raw
*
* Recebe via POST: token, extension, loadlegend, language.
* Retorna o mesmo JSON que o servidor remoto devolve (campos: license_valid, samples).
*/
public static function getThemesSamples() {
$app = Factory::getApplication();
$post = $app->input->post;
// Repassa exatamente os mesmos campos que o JS enviava direto ao servidor remoto
$dataPost = array(
'token' => $post->get('token', ''),
'extension' => $post->get('extension', ''),
'loadlegend' => $post->get('loadlegend', '0'),
'language' => $post->get('language', '*'),
'isBeta' => $post->get('isBeta', '0'),
);
$url = NbUrlUtil::getUrlNbExtensions($dataPost['isBeta']).'/index.php?option=com_nbextensoes&task=externalthemes.getThemesSamples&format=raw';
$values = new \stdClass();
$values->license_valid = '1';
$values->samples = new \stdClass();
try {
$curlRequest = NbCurlUtil::request('POST', $url, http_build_query($dataPost), null, 20);
} catch (\Throwable $e) {
// Mantem contrato esperado pelo JS: license_valid + samples
// (em caso de falha o JS exibe banner de erro via fail() e o usuario consegue prosseguir)
exit(json_encode($values));
}
// Falha de conexao ou HTTP fora do range 2xx
if (empty($curlRequest) || empty($curlRequest->success)) {
exit(json_encode($values));
}
$response = isset($curlRequest->data) ? $curlRequest->data : '';
// Servidor remoto ja devolve JSON formatado igual ao esperado pelo JS:
// { "license_valid": "0|1", "samples": { ... } }
// Repassa o conteudo cru para preservar o formato.
if (is_string($response) && $response !== '') {
exit($response);
}
exit(json_encode($values));
}
/**
* Carrega uma modal para a escolha de temas e exemplos de um modulo
*/
public static function loadModuleSample() {
$app = Factory::getApplication();
$post = $app->input->post;
// pega o idioma vindo da requisicao ajax
$langCode = $post->get('lang');
$language = Factory::getApplication()->getLanguage();
// Carrega o idioma do site
$lang = Factory::getContainer()->get(LanguageFactoryInterface::class)->createLanguage($langCode, (bool) $app->get('debug_lang'));
$app->loadLanguage($lang);
// Carrega arquivo tradução da library no boss
$lang->load('lib_noboss', JPATH_SITE.'/libraries/noboss');
$extensionToken = $post->get('token', '');
$extension = $post->get('extensionName', 'banners');
$model = $post->get('model', 'model1');
$sampleId = $post->get('sampleId', "demo_{$extension}_{$model}_default");
$itemsFormName = $post->get('itemsFormName', []);
$addModals = $post->get('addModals', []);
$fieldsNames = $post->get("fieldsNames", []);
$language = $lang->get('tag', 'en-GB');
$mainSubform = $post->getString('loadModeSubform', '');
$isBeta = $post->get('isBeta', '0');
// Ensure arrays are arrays to prevent implode errors
if (!is_array($itemsFormName)) {
$itemsFormName = [];
}
if (!is_array($addModals)) {
$addModals = [];
}
if (!is_array($fieldsNames)) {
$fieldsNames = [];
}
// TODO: codigo abaixo pode ser descomentando se quiser testar o script direto no navegador forcando uma extensao e acessando a url https://localhost/nb/extensions/pt/?option=com_nobossajax&library=noboss.src.Form.Field.Nbtheme.NbthemeHelper&method=loadModuleSample&format=raw
// $extensionToken = '536f40a0542b1ad75dfad213a4bd37ca';
// $extension = 'banners';
// $mainSubform = 'model1';
// Extensao esta sendo carregada no site demo
if (strpos(Uri::root(), 'nobossextensions.com/demos') !== false) {
$siteDemo = true;
}
else{
$siteDemo = false;
}
$url = NbUrlUtil::getUrlNbExtensions($isBeta).'/index.php?option=com_nbextensoes&task=externalthemes.getSample&format=raw';
// Cria o objeto que será retornado
$values = new \stdClass();
$values->success = 0;
// Configura dados do POST.
$dataPost = array(
'token' => $extensionToken,
'sampleId' => $sampleId,
'model' => $model,
'language' => $language,
'itemsFormName' => $itemsFormName,
'addModals' => $addModals,
'fieldsNames' => $fieldsNames,
'mainSubform' => $mainSubform,
'websiteRequest' => Uri::root()
);
$dataPost = http_build_query($dataPost);
try {
$curlRequest = NbCurlUtil::request('POST', $url, $dataPost, null, 20);
} catch (\Throwable $e) {
$values->message = $e->getMessage();
exit(json_encode($values));
}
if (!$curlRequest->success) {
$values->message = $curlRequest->message;
exit(json_encode($values));
}
$response = $curlRequest->data;
// echo '<pre>';
// var_dump($response);
// exit;
// Verifica se não deu erro de exemplo não encontrado
if (trim($response) == "SAMPLE_NOT_FOUND"){
$values->message = Text::_('LIB_NOBOSS_FIELD_NOBOSSTHEME_GENERATE_EXAMPLES_ERROR_NOT_FOUND');
exit(json_encode($values));
}
try{
// Decodifica a resposta do servidor
$response = \json_decode($response, true);
} catch (\Exception $e){
$values->message = Text::_('LIB_NOBOSS_FIELD_NOBOSSTHEME_GENERATE_EXAMPLES_ERROR_JSON_PARSE_SERVER');
exit(json_encode($values));
}
// Nenhum conteudo retornado em response
if (empty($response)){
$values->message = Text::_('LIB_NOBOSS_FIELD_NOBOSSTHEME_GENERATE_EXAMPLES_ERROR_NO_RESPONSE_FROM_SERVER');
exit(json_encode($values));
}
// Ensure response arrays are arrays to prevent implode errors
if (!is_array($response)) {
$response = [];
}
if (isset($response['items']) && !is_array($response['items'])) {
$response['items'] = [];
}
if (isset($response['fields']) && !is_array($response['fields'])) {
$response['fields'] = [];
}
if (isset($response['modals']) && !is_array($response['modals'])) {
$response['modals'] = [];
}
// Token invalido: personaliza mensagem conforme situacao
if(!$siteDemo && array_key_exists('valid_token', $response)){
// Token nao existe na base local
if($response['valid_token'] == 0){
$values->message = Text::_('LIB_NOBOSS_FIELD_NOBOSSTHEME_GENERATE_EXAMPLES_ERROR_TOKEN_NOT_VALID');
}
// A licenca do usuario nao inclui o tema escolhido
else if ($response['in_plan'] == 0){
$values->message = Text::_('LIB_NOBOSS_FIELD_NOBOSSTHEME_GENERATE_EXAMPLES_ERROR_TOKEN_PLAN_NOT_INLCUDED');
}
// Nao esta no periodo de atualizacoes
else if ($response['inside_support_updates'] == 0){
$values->message = Text::_('LIB_NOBOSS_FIELD_NOBOSSTHEME_GENERATE_EXAMPLES_ERROR_TOKEN_SUPPORT_UPDATES_EXPIRATED');
}
$values->data = "INVALID_TOKEN";
exit(json_encode($values));
}
if ($values) {
// Nome da extensao foi definido no xml
if ($extension){
// Carrega arquivo tradução da extensao em que a modal está sendo chamada
$lang->load("mod_noboss{$extension}", JPATH_ROOT."/modules/mod_noboss{$extension}");
}
// echo '<pre>';
// var_dump($response['items']);
// exit;
// Carrega o xml da extensão
$xml = simplexml_load_file(JPATH_ROOT."/modules/mod_noboss{$extension}/mod_noboss{$extension}.xml");
$fields = $xml->config->fields;
// Foi setado fields especificos a terem conteudo de exemplo copiado
if(!empty($response['fields'])){
foreach ($response['fields'] as $fieldName => $value) {
try {
$xmlField = $fields->xpath('//field[@name="'.$fieldName.'"]');
$field = $xmlField[0];
try{
// Extensao esta no novo formato p/ J5
if(is_dir(JPATH_SITE."/modules/mod_noboss{$extension}/src")){
$extensionUcFirst = ucfirst($extension);
// Monta o xml que sera usado pelo getinstance
$newXml = '<form addfieldprefix="Noboss\Library\Form\Field"><fieldset addfieldprefix="Noboss\Module\\'.$extensionUcFirst.'\Site\Field" name="basic" >'.$field->asXML().'</fieldset></form>';
//echo $newXml; exit;
}
// TODO: qnd todas extensoes estiverem migradas para novo formato do J5 podemos remover esse else e deixar soh o conteudo do if acima
// Extensao esta no formato antigo que exige plugin de compatibilidade
else{
// Monta o xml que sera usado pelo getinstance
$newXml = '<form addfieldpath="libraries/noboss/forms/fields"><fieldset addfieldpath="modules/mod_noboss'.$extension.'/fields" name="basic" >'.$field->asXML().'</fieldset></form>';
}
// Instancia o form
$formFactory = Factory::getContainer()->get(FormFactoryInterface::class);
$form = $formFactory->createForm($fieldName, array('control' => 'jform[params]'));
$form->load($newXml);
} catch (\Exception $e){
$values->message = Text::_('LIB_NOBOSS_FIELD_NOBOSSTHEME_GENERATE_EXAMPLES_ERROR_XML_INVALID');
exit(json_encode($values));
}
// // se tiver valor seta no campo
// if(!empty($value)){
// Seta os valores do field
$form->setValue($fieldName, null, ($response['fields'][$fieldName]));
// }
// adiciona na resposta o html do field
$response['fields'][$fieldName] = $form->getField($fieldName)->renderField();
} catch (\Throwable $e) {
$values->message = 'Error processing field ' . $fieldName . ': ' . $e->getMessage();
exit(json_encode($values));
}
}
}
// Foi setado ao menos um subform a ter conteudo de exemplo copiado
if(!empty($response['items'])){
foreach($response['items'] as $subformName => $val){
try{
// Extensao esta no novo formato p/ J5
if(is_dir(JPATH_SITE."/modules/mod_noboss{$extension}/src")){
$xmlSubform = $fields->xpath('//field[@name="'.$subformName.'" and starts-with(@type,"NbSubform")]');
$subform = $xmlSubform[0];
$extensionUcFirst = ucfirst($extension);
// Monta o xml que sera usado pelo getinstance
$newXml = '<form addfieldprefix="Noboss\Library\Form\Field"><fieldset addfieldprefix="Noboss\Module\\'.$extensionUcFirst.'\Site\Field" name="basic" >'.$subform->asXML().'</fieldset></form>';
//echo $newXml; exit;
}
// TODO: qnd todas extensoes estiverem migradas para novo formato do J5 podemos remover esse else e deixar soh o conteudo do if acima
// Extensao esta no formato antigo que exige plugin de compatibilidade
else{
$xmlSubform = $fields->xpath('//field[@name="'.$subformName.'" and starts-with(@type,"nobosssubform")]');
$subform = $xmlSubform[0];
// Monta o xml que sera usado pelo getinstance
$newXml = '<form addfieldpath="libraries/noboss/forms/fields"><fieldset addfieldpath="modules/mod_noboss'.$extension.'/fields" name="basic" >'.$subform->asXML().'</fieldset></form>';
}
// Instancia o formulário
$formFactory = Factory::getContainer()->get(FormFactoryInterface::class);
$form = $formFactory->createForm($subformName, array('control' => 'jform[params]'));
$form->load($newXml);
} catch (\Exception $e){
$values->message = Text::_('LIB_NOBOSS_FIELD_NOBOSSTHEME_GENERATE_EXAMPLES_ERROR_XML_INVALID');
exit(json_encode($values));
}
try {
// Seta os valores do subform
$subformValue = $response['items'][$subformName];
if (!is_array($subformValue) || empty($subformValue)) {
$subformValue = null; // Let subform use default empty row
}
$form->setValue($subformName, null, $subformValue);
$response['items'][$subformName] = $form->getField($subformName)->renderField();
} catch (\Throwable $e) {
$values->message = 'Error processing subform ' . $subformName . ': ' . $e->getMessage();
exit(json_encode($values));
}
}
}
$values->success = 1;
$values->message = Text::_('LIB_NOBOSS_FIELD_NOBOSSTHEME_GENERATE_EXAMPLES_SUCCESS');
$values->data = $response;
}
exit(json_encode($values));
}
/**
* Carrega uma modal para a escolha de temas e exemplos de um componente
*/
public static function loadComponentSample() {
$app = Factory::getApplication();
$post = $app->input->post;
// pega o idioma vindo da requisicao ajax
$langCode = $post->get('lang');
$language = Factory::getApplication()->getLanguage();
// Carrega o idioma do site
$lang = Factory::getContainer()->get(LanguageFactoryInterface::class)->createLanguage($langCode, (bool) $app->get('debug_lang'));
$app->loadLanguage($lang);
// Carrega arquivo tradução da library no boss
$lang->load('lib_noboss', JPATH_SITE.'/libraries/noboss');
// Token da extensão
$extensionToken = $post->get('token');
// Pega o nome da extensão
$extension = $post->get('extensionName', '');
// Nome da extensao foi definido no xml
if ($extension){
// Carrega arquivo tradução da extensao em que a modal está sendo chamada
$lang->load("com_noboss{$extension}", JPATH_ROOT."/administrator/components/com_noboss{$extension}");
}
// Pega o nome do tema escolhido
$model = $post->get('model', 'model1');
// Pega o modelo da extensão
$sampleId = $post->get('sampleId', "demo_{$extension}_{$model}_default");
// Pega a a tag de linguagem atual
$language = Factory::getApplication()->getLanguage()->get('tag');
$isBeta = $post->get('isBeta', '0');
$url = NbUrlUtil::getUrlNbExtensions($isBeta).'/index.php?option=com_nbextensoes&task=externalthemes.getSample&format=raw';
// Cria o objeto que será retornado
$values = new \stdClass();
$values->success = 0;
// Configura dados do POST.
$dataPost = array(
'token' => $extensionToken,
'sampleId' => $sampleId,
'model' => $model,
'language' => $language,
'component' => true
);
// Faz uma requisição curl usando a library
try {
$curlRequest = NbCurlUtil::request('POST', $url, $dataPost, null, 20);
} catch (\Throwable $e) {
$values->message = $e->getMessage();
exit(json_encode($values));
}
if (!$curlRequest->success) {
$values->message = $curlRequest->message;
exit(json_encode($values));
}
$rawBody = $curlRequest->data;
$response = \json_decode($rawBody, true);
$jsonError = \json_last_error();
if ($jsonError !== JSON_ERROR_NONE) {
$values->message = Text::_('LIB_NOBOSS_FIELD_NOBOSSTHEME_GENERATE_EXAMPLES_ERROR_JSON_PARSE_SERVER');
exit(\json_encode($values));
}
// Remoto: json_encode(false) quando getSample nao encontra modulo na posicao (ver ExternalthemesModel::getSample)
if ($response === false) {
$values->message = Text::_('LIB_NOBOSS_FIELD_NOBOSSTHEME_GENERATE_EXAMPLES_ERROR_NOT_FOUND');
exit(\json_encode($values));
}
if (!\is_array($response)) {
if (\is_string($response) && $response !== '') {
$again = \json_decode($response, true);
if (\json_last_error() === JSON_ERROR_NONE && \is_array($again)) {
$response = $again;
}
}
}
if (!\is_array($response)) {
$response = [];
}
// Params as vezes vêm aninhados em "params" (Registry / export)
if (isset($response['params']) && (\is_array($response['params']) || \is_object($response['params']))) {
$nested = (array) $response['params'];
unset($response['params']);
$response = \array_merge($response, $nested);
}
if (isset($response['fields']) && !\is_array($response['fields'])) {
$response['fields'] = [];
}
if (isset($response['modals']) && !\is_array($response['modals'])) {
$response['modals'] = [];
}
$flatModalKeyPattern = '/_display_(external_area|items_customization|search_form|categories)_/i';
$metaKeys = [
'valid_token', 'in_plan', 'inside_support_updates',
'module_tag', 'header_tag', 'modals', 'fields', 'modalsJson',
'items', 'itemsloadmode',
];
// Resposta de modulos usa "modals"; o JS (nobosstheme.js) so le "modalsJson"
if (!empty($response['modals']) && empty($response['modalsJson'])) {
$response['modalsJson'] = $response['modals'];
}
// component=true: JSON plano dos params — nao exigir ausencia de "fields" (pode existir nos params do modulo)
if (empty($response['modalsJson'])) {
$fromFlat = [];
foreach ($response as $key => $value) {
if (!\is_string($key) || \in_array($key, $metaKeys, true)) {
continue;
}
if (\preg_match($flatModalKeyPattern, $key)) {
$fromFlat[$key] = (\is_array($value) || \is_object($value))
? \json_encode($value, JSON_UNESCAPED_UNICODE)
: $value;
}
}
if (!empty($fromFlat)) {
$response['modalsJson'] = $fromFlat;
}
}
$fieldsExisting = (isset($response['fields']) && \is_array($response['fields'])) ? $response['fields'] : [];
if (\count($fieldsExisting) === 0) {
$fields = [];
$modalIndex = \array_flip(\array_keys($response['modalsJson'] ?? []));
foreach ($response as $key => $value) {
if (!\is_string($key) || \in_array($key, $metaKeys, true)) {
continue;
}
if (isset($modalIndex[$key])) {
continue;
}
$fields[$key] = $value;
}
$response['fields'] = $fields;
}
if (!isset($response['modalsJson']) || !\is_array($response['modalsJson'])) {
$response['modalsJson'] = [];
}
if (!isset($response['fields']) || !\is_array($response['fields'])) {
$response['fields'] = [];
}
// Verifica se o token é invalido ou o periodo de suporte expirou
if(array_key_exists('valid_token', $response)){
// Varifica se o token existe
if($response['valid_token'] == 0){
$values->message = Text::_('LIB_NOBOSS_FIELD_NOBOSSTHEME_GENERATE_EXAMPLES_ERROR_TOKEN_NOT_VALID');
// Verifica se o plano inclui o modelo
} else if ($response['in_plan'] == 0){
$values->message = Text::_('LIB_NOBOSS_FIELD_NOBOSSTHEME_GENERATE_EXAMPLES_ERROR_TOKEN_PLAN_NOT_INLCUDED');
// Caso seja custom, verifica se está no periodo do suporte
} else if ($response['inside_support_updates'] == 0){
$values->message = Text::_('LIB_NOBOSS_FIELD_NOBOSSTHEME_GENERATE_EXAMPLES_ERROR_TOKEN_SUPPORT_UPDATES_EXPIRATED');
}
$values->data = "INVALID_TOKEN";
exit(json_encode($values));
}
// Carrega o XML do formulario do componente (J5: forms/*.xml; legado: models/forms/group.xml)
$componentBase = Path::clean(JPATH_ADMINISTRATOR . '/components/com_noboss' . $extension);
$extensionLower = strtolower((string) $extension);
$formXmlCandidates = [
$componentBase . '/forms/' . $extensionLower . '.xml',
$componentBase . '/forms/faq.xml',
$componentBase . '/forms/group.xml',
$componentBase . '/models/forms/group.xml',
];
$formXmlPath = null;
foreach ($formXmlCandidates as $candidate) {
if (\is_file($candidate)) {
$formXmlPath = $candidate;
break;
}
}
if ($formXmlPath === null) {
$values->message = Text::_('LIB_NOBOSS_FIELD_NOBOSSTHEME_GENERATE_EXAMPLES_ERROR_XML_INVALID');
exit(json_encode($values));
}
$xml = \simplexml_load_file($formXmlPath);
if ($xml === false) {
$values->message = Text::_('LIB_NOBOSS_FIELD_NOBOSSTHEME_GENERATE_EXAMPLES_ERROR_XML_INVALID');
exit(json_encode($values));
}
try{
// Extensao esta no novo formato p/ J5
if(is_dir(JPATH_ROOT."/administrator/components/com_noboss{$extension}/src")){
$extensionUcFirst = ucfirst($extension);
// Monta inicio do xml que sera usado pelo getinstance
$xmlFields = '<form addfieldprefix="Noboss\\Library\\Form\\Field"><fieldset addfieldprefix="Noboss\\Component\\' . $extensionUcFirst . '\\Administrator\\Field" name="basic" >';
//echo $xmlFields; exit;
}
// TODO: qnd todas extensoes estiverem migradas para novo formato do J5 podemos remover esse else e deixar soh o conteudo do if acima
// Extensao esta no formato antigo que exige plugin de compatibilidade
else{
// Monta inicio do xml que sera usado pelo getinstance
$xmlFields = '<form addfieldpath="libraries/noboss/forms/fields"><fieldset addfieldpath="administrator/components/com_noboss'.$extension.'/models/fields" name="basic" >';
}
// Percorre todos os fields a serem adicionados no form
foreach ($response['fields'] as $key => $value) {
try {
$field = $xml->xpath('//field[@name="'.$key.'"]');
$field = $field[0];
if(!empty($field)){
$xmlFields .= $field->asXML();
}
} catch (\Throwable $e) {
$values->message = 'Error processing field ' . $key . ': ' . $e->getMessage();
exit(json_encode($values));
}
}
$xmlFields .= "</fieldset></form>";
// Instancia o formulario
$formFactory = Factory::getContainer()->get(FormFactoryInterface::class);
$form = $formFactory->createForm('sample_data', array('control' => 'jform'));
$form->load($xmlFields);
} catch (\Exception $e){
$values->message = Text::_('LIB_NOBOSS_FIELD_NOBOSSTHEME_GENERATE_EXAMPLES_ERROR_XML_INVALID');
exit(json_encode($values));
}
// percorre cada campo de dado de exemplo
foreach ($response['fields'] as $key => $value) {
try {
// pega o objeto form do campo atual
$field = $form->getField($key);
if(!empty($field)){
// seta o valor
$field->setValue($value);
// renderiza o html desse field na resposta da requisicao
$response['fields'][$key] = $field->renderField();
}
else{
unset($response['fields'][$key]);
}
} catch (\Throwable $e) {
$values->message = 'Error processing field ' . $key . ': ' . $e->getMessage();
exit(json_encode($values));
}
}
if ($values) {
// seta como sucesso a requisicao
$values->success = 1;
// mensagem de sucesso
$values->message = Text::_('LIB_NOBOSS_FIELD_NOBOSSTHEME_GENERATE_EXAMPLES_SUCCESS');
// adiciona os json das modais no valor que sera retornado
$values->data = $response;
}
// Retorna em formato json
exit(json_encode($values));
}
}