-
Cómo funcionan las Mision...
Foro: Guías y Tutoriales
Último mensaje por: Tronlar
06-14-2026, 09:46 PM
» Respuestas: 0
» Vistas: 105 -
V6 Original/Dark/Memes (A...
Foro: Diseños Terminados
Último mensaje por: carlos007r
06-14-2026, 11:08 AM
» Respuestas: 26
» Vistas: 6,357 -
V5
Foro: Diseños Terminados
Último mensaje por: Aeikox
06-13-2026, 12:16 AM
» Respuestas: 8
» Vistas: 1,452 -
Risus 1.3 Actualizado jQu...
Foro: Risus 1.3
Último mensaje por: Tronlar
06-12-2026, 10:45 PM
» Respuestas: 55
» Vistas: 10,672 -
Preguntas Frecuentes y So...
Foro: Guías y Tutoriales
Último mensaje por: Tronlar
06-12-2026, 06:17 PM
» Respuestas: 0
» Vistas: 83 -
Cómo registrar tu comunid...
Foro: Guías y Tutoriales
Último mensaje por: Tronlar
06-12-2026, 05:32 PM
» Respuestas: 0
» Vistas: 62 -
Cómo monetizar tu comunid...
Foro: Guías y Tutoriales
Último mensaje por: Tronlar
06-11-2026, 07:19 PM
» Respuestas: 0
» Vistas: 73 -
Introducción al SEO para ...
Foro: Guías y Tutoriales
Último mensaje por: Tronlar
06-11-2026, 06:58 PM
» Respuestas: 0
» Vistas: 82 -
Guía completa de SEO para...
Foro: Guías y Tutoriales
Último mensaje por: Tronlar
06-11-2026, 06:48 PM
» Respuestas: 0
» Vistas: 95 -
Diccionario de términos b...
Foro: Guías y Tutoriales
Último mensaje por: Tronlar
06-11-2026, 01:41 PM
» Respuestas: 0
» Vistas: 115
- Mensajes del foro:1,957
- Temas del foro:614
- Miembros:1,036
- Último miembro:carlos007r
Bueno lo que trate de hacer es simplificar un poco, quieres agregar una imagen a "rangos", "categorías" o "medallas" y no quieres entrar desde el FTP o CPanel, bueno con esta herramienta deberías poder realizar esta tarea desde la administración de tu sitio.
Les mostraré algunas imágenes, pero no esta realizado con el default, si no que es una versión que estoy realizando.
[img]Registrate o inicia tu sesión para ver este contenido[/img]
[img]Registrate o inicia tu sesión para ver este contenido[/img]
[img]Registrate o inicia tu sesión para ver este contenido[/img]
Bueno comenzamos...
1 - En inc/class/c.admin.php lo pueden agregar al final o donde quieran.
# Obtenemos todas las imagenes de la carpeta
public function obtener_paquete($dir) {
global $tsCore, $smarty;
# Aplicamos los permisos si no los tiene
foreach (["cat", "med", "ran"] as $carpeta) chmod($smarty->template_dir["images"] . 'icons/' . $carpeta, 0777);
# Creamos un arreglo
$data = [];
# Buscamos en la carpeta...
$carpeta = $smarty->template_dir["images"] . 'icons/' . $dir;
$imagenes = opendir( $carpeta );
# Recorremos la carpeta
while ($icono = readdir($imagenes)) {
if($icono != '.' && $icono != '..'):
# Obtenemos información del archivo
$inf = getimagesize($carpeta . '/' . $icono);
$arr = [
"hash" => substr(md5(explode('.', $icono)[0]), 0, 6),
"icon" => explode('.', $icono)[0],
"url" => $tsCore->settings["images"] . '/icons/' . $dir . '/' .$icono,
"width" => $inf[0],
"height" => $inf[1],
"type" => $inf["mime"]
];
if(isset($_GET["size"]) or isset($_GET["type"])) {
if(intval($_GET["size"]) === $inf[0] or 'image/'.$_GET["type"] === $inf["mime"]) {
array_push($data, $arr);
}
} else array_push($data, $arr);
endif;
}
closedir( $imagenes );
# Retornamos el arreglo
return $data;
}
public function eliminar_icono_paquete() {
global $tsCore, $smarty;
# Buscamos en la carpeta...
$carpeta = $smarty->template_dir["images"] . 'icons/' . $tsCore->setSecure($_POST["path"]);
# Eliminamos si son la misma imagen con difente tamaño
foreach($this->obtener_paquete($_POST["path"]) as $eliminar) {
if($eliminar["hash"] === $_POST["hash"]) {
if($_POST["path"] === 'med') {
$sizes = [16, 32];
foreach ($sizes as $size) {
$nimg = "{$eliminar["icon"]}_{$size}." . substr($eliminar["type"], 6);
unlink($carpeta . '/' . $nimg);
}
return true;
} else {
$nimg = "{$carpeta}/{$eliminar["icon"]}." . substr($eliminar["type"], 6);
return (unlink($nimg)) ? true : false;
}
}
}
}
public function subir_icono() {
global $smarty;
# Mover a...
$mover = $smarty->template_dir["images"] . 'icons/' . $_POST["path"] . DIRECTORY_SEPARATOR;
//
if (($_FILES["file"]["type"] == "image/jpg") || ($_FILES["file"]["type"] == "image/png") || ($_FILES["file"]["type"] == "image/gif")) {
# Agregamos la imagen, si existe, esta será reemplazada por la nueva
return (move_uploaded_file($_FILES["file"]["tmp_name"], $mover . $_FILES['file']['name'])) ? '1: Imagen agregada correctamente...' : '0: No se pudo subir la imagen!';
} else return false;
}
Este código es para Smarty 4, si no lo tienes actualizado, lo que debes hacer es lo siguiente, buscas esto
$smarty->template_dir["images"] . 'icons/'
y lo reemplazas por
TS_ROOT . '/themes/' . $tsCore->settings["tema"]['t_path'] . '/images/icons/'
y borrar el de las funciones, excepto el que lleva $tsCore, allí solo eliminas $smarty
global $smarty;
2 - En inc/php/admin.php buscan
} elseif($action == 'creditos'){
y arriba agregan
# PACKS
} elseif($action == 'packs') {
$smarty->assign('tsDir', $_GET["path"]);
if($act === 'abrir') {
$smarty->assign('tsPack', $tsAdmin->obtener_paquete($_GET["path"]));
}
3 - En inc/php/ajax/ajax.admin.php buscan
'admin-badwords-delete' => array('n' => 4, 'p' => ''),
y debajo pegan
'admin-eliminar-icono' => array('n' => 4, 'p' => ''),
'admin-subir-icono' => array('n' => 4, 'p' => ''),
más abajo
default:
die('0: Este archivo no existe.');
break;
arriba agregan
case 'admin-eliminar-icono':
echo $tsAdmin->eliminar_icono_paquete();
break;
case 'admin-subir-icono':
echo $tsAdmin->subir_icono();
break;
4 - En tema/templates/admin_mods/ crean un archivo llamado "m.admin_packs.tpl" y dentro agregan, les recuerdo que lo pueden adaptar a su theme
<div class="boxy-title">
<h3>Control de paquetes de imagenes</h3>
</div>
<div id="res" class="boxy-content">
<p class="alerts ok">Desde aquí podrás ver, agregar, eliminar las imagenes e iconos que estan guardados en "<a href="{$tsConfig.url}/admin/packs?act=abrir&path=ran">ran</a>", "<a href="{$tsConfig.url}/admin/packs?act=abrir&path=med">med</a>", "<a href="{$tsConfig.url}/admin/packs?act=abrir&path=cat">cat</a>".</p>
{if $tsSave}<div class="alerts ok">Tus cambios han sido guardados.</div>{/if}
<hr class="separator">
{if $tsAct === ''}
<div style="display:grid;gap:10px;grid-template-columns: repeat(3, 1fr);">
<a href="{$tsConfig.url}/admin/packs?act=abrir&path=cat" class="block text-center">
<img width="140" height="140" src="{$tsConfig.images}/category.svg" alt="Categorías">
<strong style="margin-top:4px;display: block;">Categorías</strong>
</a>
<a href="{$tsConfig.url}/admin/packs?act=abrir&path=med" class="block text-center">
<img width="140" height="140" src="{$tsConfig.images}/award.svg" alt="Medallas">
<strong style="margin-top:4px;display: block;">Medallas</strong>
</a>
<a href="{$tsConfig.url}/admin/packs?act=abrir&path=ran" class="block text-center">
<img width="140" height="140" src="{$tsConfig.images}/ran.svg" alt="Rangos">
<strong style="margin-top:4px;display: block;">Rangos</strong>
</a>
</div>
{elseif $tsAct === 'abrir'}
{if $tsDir === 'med' || $tsDir === 'ran'}
<span>Filtros:</span>
{if $tsDir === 'med'}
<a href="{$tsConfig.url}/admin/packs?act=abrir&path=med" style="text-align:center;">Todos</a> -
<a href="{$tsConfig.url}/admin/packs?act=abrir&path=med&size=16" style="text-align:center;">16x16</a> -
<a href="{$tsConfig.url}/admin/packs?act=abrir&path=med&size=32" style="text-align:center;">32x32</a>
{/if}
{if $tsDir === 'ran'}
<a href="{$tsConfig.url}/admin/packs?act=abrir&path=ran" style="text-align:center;">Todos</a> -
<a href="{$tsConfig.url}/admin/packs?act=abrir&path=ran&type=gif" style="text-align:center;">GIF</a> -
<a href="{$tsConfig.url}/admin/packs?act=abrir&path=ran&type=png" style="text-align:center;">PNG</a>
{/if}
{/if}
<table class="admin_table">
<thead>
<tr>
<th>Icono</th>
<th>Nombre</th>
<th>Dimensiones</th>
<th>Tipo</th>
<th>Acciones</th>
</tr>
</thead>
{foreach $tsPack item=ic}
<tr class="{$ic.hash}">
<td style="text-align:center;"><img src="{$ic.url}" alt="{$ic.icon}"></td>
<td style="width: max-content">{$ic.icon}</td>
<td>{$ic.width}x{$ic.height}</td>
<td>{$ic.type}</td>
<td class="admin_actions flex jcsaround aicenter">
<a href="javascript:packs.borrar('{$tsDir}', '{$ic.hash}')" title="Eliminar"><i class="fas fa-trash"></i></a>
</td>
</tr>
{/foreach}
</table>
<hr class="separator">
<div style="text-align:center;">
<a href="{$tsConfig.url}/admin/packs?act=agregar&path={$tsDir}" class="mBtn">Agregar icono en {$tsDir}</a>
</div>
{elseif $tsAct === 'agregar'}
<form method="post" enctype="multipart/form-data">
<input type="hidden" name="path" id="path" value="{$tsDir}">
<div class="form-line">
<label for="image">Sube una imagen...</label>
<input type="file" class="form-control-file" name="image" id="image">
</div>
<p><a href="javascript:packs.subir()" class="mBtn btnOk">Agregar</a></p>
</form>
{/if}
</div>5 - En tema/templates/admin_mods/m.admin_sidemenu.tpl buscan
<li id="a_badwords"><span class="cat-title"><a href="{$tsConfig.url}/admin/badwords">Censuras</a></span></li>y debajo agregan
<li id="a_main"><span class="cat-title"><a href="{$tsConfig.url}/admin/packs">Control de iconos</a></span></li>6 - En tema/templates/t.admin.tpl debajo de
{include file='admin_mods/m.admin_rangos.tpl'}
agregar
{elseif $tsAction == 'packs'}
{include file='admin_mods/m.admin_packs.tpl'}
7 - En tema/js/admin.js al final del archivo agregan
/**
* Control de paquete
* para agregar, editar y eliminar iconos
*/
var packs = new function() {
this.reload = path => location.href = global_data.url + '/admin/packs?act=abrir&path=' + path,
this.subir = () => {
var formData = new FormData();
formData.append('file', $('#image')[0].files[0]);
formData.append('path', $('#path').val());
$.ajax({
url: global_data.url + '/admin-subir-icono.php',
type: 'post',
data: formData,
contentType: false,
processData: false,
success: response => {
switch(response.charAt(0)) {
case '0':
mydialog.alert('Error', response.substring(3), false);
mydialog.center();
break;
case '1':
mydialog.show()
mydialog.title('Bien!')
mydialog.body(response.substring(3))
mydialog.buttons(true, true, 'Continuar', 'packs.reload(' + carpeta + ')', true, false, false);
/*mydialog.buttons({mostrar:true,texto:'Continuar',accion:`packs.reload('${path}')`,activo:true});*/
mydialog.center()
break;
}
}
});
return false;
},
this.borrar = (carpeta, hash, status) => {
if(!status) {
mydialog.show()
mydialog.title('¿Deseas eliminar ' + (carpeta == 'med' ? 'estos iconos' : 'este icono') + '?')
mydialog.body('Esto eliminará el/los iconos de su tema')
mydialog.buttons(true, true, 'Continuar', 'packs.borrar(' + carpeta + ', ' + hash + ', true)', true, false, true, 'No', 'close', true, true);
/*mydialog.buttons([
{mostrar:true,texto:'Continuar',accion:`packs.borrar('${carpeta}', '${hash}', true)`,activo:true},
{mostrar:true,texto:'No',accion:'cerrar',activo:true}
]);*/
mydialog.center()
} else {
var params = ['path=' + carpeta, 'hash=' + hash].join('&')
$.post(global_data.url + '/admin-eliminar-icono.php', params, del => {
mydialog.close();
if(del) {
if(carpeta === 'med') {
$("tr." + hash).each( (inx, trh) => trh.remove())
} else $("tr." + hash).remove()
}
})
}
}
}8 - Suben estas 3 imágenes a tema/images/
Para la próxima le quiero agregar un paginador, y la subida de múltiples imágenes...
NOTA: Para que funciones correctamente, el complemento le asignará a las carpetas ran, cat y med los permisos necesarios para que funcione, en caso que tenga error o no suba las imágenes a dichas carpetas lo que deben hacer es darle permisos 0777 manualmente a las carpetas mencionadas
En este caso se usará CDN para que sea mejor
1 - En inc/class/c.admin.php buscamos
function saveOrden() { ... todo ... }
y lo reemplazamos por
public function saveOrden() {
global $tsCore;
#
$ordenado = [];
# Obtenemos lista con el nuevo orden
$nuevo_orden = 1;
foreach (explode(',', $_POST["cats"]) as $orden) {
db_exec([__FILE__, __LINE__], 'query', "UPDATE p_categorias SET c_orden = ".$nuevo_orden." WHERE cid = ".$orden);
array_push($ordenado, $nuevo_orden);
$nuevo_orden++;
}
}
2 - En inc/php/ajax/ajax.admin.php buscan
'admin-badwords-delete' => array('n' => 4, 'p' => ''),
y debajo agregan
'admin-ordenar-categorias' => array('n' => 4, 'p' => ''),
más abajo buscan
case 'admin-badwords-delete':
//<---
echo $tsAdmin->deleteBadWord();
//--->
break;
y debajo agregan
case 'admin-ordenar-categorias':
//<---
echo $tsAdmin->saveOrden();
//--->
break;
3 - Luego en tema/templates/admin_mods/m.admin_cats.tpl y buscamos, puedes eliminar jquery.tablednd.js si quieres
<script type="text/javascript" src="{$tsConfig.js}/jquery.tablednd.js"></script>y lo reemplazamos por, se usa la condicional ya que solo es donde se muestra la lista de categorías
{if $tsAct == ''}
<script src="Registrate o inicia tu sesión para ver este contenido@latest/Sortable.min.js"></script>
{/if}abajo de eso buscamos
<script type="text/javascript">
// {literal}
$(function(){
// {/literal} {if $tsAct == ''} {literal}
$('#cats_orden').tableDnD({
onDrop: function(table, row) {
$.ajax({
type: 'post',
url: global_data.url + '/admin/cats?ajax=true&ordenar=true',
cache: false,
data: $.tableDnD.serialize()
});
}
});
// {/literal} {/if} {literal}
$('#cats_orden').tableDnD({
onDrop: function(table, row) {
$.ajax({
type: 'post',
url: global_data.url + '/admin/cats?ajax=true&ordenar=true&t=cat',
cache: false,
data: $.tableDnD.serialize()
});
}
});
//
$('#cat_img').change(function(){
var cssi = $("#cat_img option:selected").css('background');
$('#c_icon').css({"background" : cssi});
});
//
});
//{/literal}
</script>y lo reemplazamos por
{if $tsAct == '' || $tsAct == 'editar' || $tsAct == 'nueva'}
<script>
$(() => {
/* {if $tsAct == ''} */
new Sortable(document.getElementById('cats_orden'), {
animation: 150,
dragClass: "arrastrar", // Clase que puedes modificar
selectedClass: "seleccionado", // Clase que puedes modificar
store: {
// Guardar orden
set: sortable => $.post(global_data.url + '/admin-ordenar-categorias.php', 'cats=' + sortable.toArray().join(','))
}
});
/* {/if} */
$('#cat_img').on('change', () => {
$('#c_icon').css({
"background": $("#cat_img option:selected").css('background')
})
});
})
</script>
{/if}más abajo buscan y le borran id="cats_orden"
<table cellpadding="0" cellspacing="0" border="0" width="500" align="center" class="admin_table" id="cats_orden">un poco más abajo buscan y le añaden id="cats_orden"
<tbody>abajo buscan
<tr id="{$c.cid}">y lo reemplazan por
<tr id="{$c.cid}" data-id="{$c.cid}">Si quieres puedes añadir estas líneas en tema/css/admin.css
.arrastrar {
background-color: #EEE;
}
.seleccionado {
background-color: #CCC;
}Si quieres obtener más información sobre el complemento "SortableJS", puedes acceder a la página que contiene la documentación para que puedas hacer los cambios que desees!
En este caso se usará CDN para que sea mejor
1 - En tema/templates/modules/m.cuenta_sidebar.tpl y reemplazamos todo por esto
<div class="sidebar-tabs *****">
<h3>Mi Avatar</h3>
<div class="avatar-big-cont">
<div style="display: none" class="avatar-loading"></div>
<img width="120" height="120" alt="" src="{$tsConfig.url}/files/avatar/{if $tsPerfil.p_avatar}{$tsPerfil.user_id}_120{else}avatar{/if}.jpg?t={$smarty.now}" class="avatar-big" id="avatar-img"/>
</div>
<ul class="change-avatar" id="change">
<li class="local-file" id="pc" style="width: 50%;text-align:center;"><span>Local</span></li>
<li class="url-file" id="url" style="width: 50%;text-align:center;"><span>Url</span></li>
</ul>
<div class="clearfix"></div>
<a href="javascript:avatar.subir()" class="avatar-next edit" >Editar</a>
</div>
<div class="clearfix"></div>
<link rel="stylesheet" href="Registrate o inicia tu sesión para ver este contenido@2.3.1/dist/croppr.min.css" integrity="sha256-Bbkel8+0sOmrvX75oDwNElgbmrAP+Pw+XXKKUwoKiVE=" crossorigin="anonymous">
<script src="Registrate o inicia tu sesión para ver este contenido@2.3.1/dist/croppr.min.js" integrity="sha256-VPADQYvd0gjLaeduvmP9/UZAdNW3D2sJieeJ3a3PX64=" crossorigin="anonymous"></script>
<script src="{$tsConfig.js}/subir-avatar.js?{$smarty.now}"></script>2 - Creamos un archivo llamado "subir-avatar.js" y lo agregamos en tema/js 12.04.2024
const next = $(".avatar-next");
const sizeImg = 120;
function cambiarFile(){
const input = $('#file-avatar')[0];
if(input.files && input.files[0]) {
let name_file = decodeURIComponent(input.files[0].name);
document.querySelector(".drop-message").innerHTML = name_file;
next.removeClass('btn-disabled')
}
}
$("#url-avatar").on('keyup', () => {
if($("#url-avatar").val().length > 5) next.removeClass('btn-disabled')
})
$("#change > li, #change > li span").on('click', event => {
const block = $("#change")
block.attr('class', '')
var tipo = event.target.textContent
if(tipo === 'Local') {
block.html(`<div id="drop-region">
<input type="file" name="local" id="file-avatar" onchange="return cambiarFile();" class="browse"/>
<div class="drop-message">
Arrastra y suelta la imagen o haz clic para subir
</div>
</div>`)
} else {
block.html(`<div style="margin: 0 auto 10px auto;">
<input type="text" name="url" autocomplete="off" id="url-avatar" placeholder="Url de la imagen" class="browse form-control"/>
</div>`)
}
})
var avatar = {
size: 120,
uid: false,
key: false,
ext: false,
informacion: '',
current: false,
success: false,
subir: async () => {
$(".avatar-loading").show().css('display', 'flex');
inputs = [].slice.call(document.querySelectorAll(".browse"))
inputs.forEach(input => {
const datoUrl = new FormData();
datoUrl.append('url', (input.name == 'url') ? input.value : input.files[0])
if(!empty(input.value)) {
fetch(global_data.url + '/upload-avatar.php', {
method: 'POST',
body: datoUrl
})
.then(response => response.json())
.then(blobData => {
avatar.subida_exitosa(blobData)
});
}
})
},
subida_exitosa: rsp => {
if (rsp.error == 'success') avatar.success = true;
else if (rsp.msg) {
avatar.key = rsp.key;
avatar.ext = rsp.ext;
avatar.cortar(rsp.msg);
} else cuenta.enviar_alerta(rsp.error, 0);
$(".avatar-loading").hide();
},
cortar: img => {
img = img + '?t=' + new Date();
mydialog.show(true);
mydialog.title("Cortar avatar");
mydialog.body(`<img class="avatar-cortar" src="${img}" />`);
mydialog.buttons(true, true, 'Cortar', "avatar.guardar()", true, false, true, 'Cancelar', 'close', true, true);
/*mydialog.buttons([
{mostrar: true, texto: 'Cortar', accion: `avatar.guardar()`, activo: true},
{mostrar: true, texto: 'Cancelar', accion: 'cerrar', activo: true}
]);*/
mydialog.center();
$("#avatar-img, #avatar-menu").attr("src", img).on('load', () => {
let sizes = [avatar.size, avatar.size, 'px'];
var croppr = new Croppr('.avatar-cortar', {
aspectRatio: 1, // Mantemos el tamanio cuadrado 1:1
startSize: sizes,
minSize: sizes,
// Enviamos las coordenadas para cortar la imagen
// Tiene la funcion onCropEnd ya que es como va a quedar
onCropEnd: data => avatar.informacion = data,
onCropMove: avatar.vistaPrevia
});
});
},
vistaPrevia: function (coords) {
let rx = avatar.size / coords.width;
let ry = avatar.size / coords.height;
$('#avatar-img').css({
width: Math.round(rx * coords.width) + 'px',
height: Math.round(ry * coords.height) + 'px',
marginLeft: '-' + Math.ceil(rx * coords.x) + 'px',
marginTop: '-' + Math.round(ry * coords.y) + 'px'
});
},
recargar: () => $("#avatar-img").attr("src", avatar.current + '?r' + new Date()),
guardar: () => {
if (empty(avatar.informacion))
cuenta.enviar_alerta('Debes seleccionar una parte de la foto', 0);
else {
const coordenadas = new FormData();
coordenadas.append('key', avatar.key)
coordenadas.append('ext', avatar.ext)
coordenadas.append('x', avatar.informacion.x)
coordenadas.append('y', avatar.informacion.y)
coordenadas.append('w', avatar.informacion.width)
coordenadas.append('h', avatar.informacion.height)
fetch(global_data.url + '/upload-crop.php', {
method: 'POST',
body: coordenadas
})
.then(response => response.json())
.then(blobData => {
if(blobData.error == "success") {
mydialog.body("Tu avatar se ha creado correctamente, ahora espera que recargue la página");
mydialog.buttons(true, true, 'Aceptar', 'close', true, true, false);
$("#input_add").hide();
$(`input[name="url"]`).attr({
value: ''
})
}
});
}
}
}3 - Luego en tema/js/cuenta.js buscar
var avatar = { .....y borran todo hasta el final del archivo
4 - Luego van a inc/php/ajax.upload.php y lo reemplazan por esto
<?php if ( ! defined('TS_HEADER')) exit('No se permite el acceso directo al script');
/**
* Controlador AJAX
*
* @name ajax.upload.php
* @author PHPost Team
*/
/**********************************\
* (VARIABLES POR DEFAULT) *
\*********************************/
// NIVELES DE ACCESO Y PLANTILLAS DE CADA ACCIÓN
$files = array(
'upload-avatar' => array('n' => 2, 'p' => ''),
'upload-crop' => array('n' => 2, 'p' => ''),
'upload-images' => array('n' => 2, 'p' => ''),
);
/**********************************\
* (VARIABLES LOCALES ESTE ARCHIVO) *
\*********************************/
// REDEFINIR VARIABLES
$tsPage = 'php_files/p.upload.'.$files[$action]['p'];
$tsLevel = $files[$action]['n'];
$tsAjax = empty($files[$action]['p']) ? 1 : 0;
/**********************************\
* (INSTRUCCIONES DE CODIGO) *
\*********************************/
// DEPENDE EL NIVEL
$tsLevelMsg = $tsCore->setLevel($tsLevel, true);
if($tsLevelMsg != 1) { echo '0: '.$tsLevelMsg['mensaje']; die();}
// CLASE
require('../class/c.upload.php');
$tsUpload = new tsUpload();
// CODIGO
switch($action){
case 'upload-avatar':
// <--
$tsUpload->image_scale = true;
$tsUpload->image_size['w'] = 640;
$tsUpload->image_size['h'] = 480;
//
$tsUpload->file_url = $_POST['url'];
//
$result = $tsUpload->newUpload(3);
echo json_encode($result);
// -->
break;
case 'upload-crop':
// <--
echo json_encode($tsUpload->cropAvatar($tsUser->uid));
// PARA EL PERFIL
db_exec(array(__FILE__, __LINE__), 'query', 'UPDATE u_perfil SET p_avatar = 1 WHERE user_id = ' . $tsUser->uid);
// -->
break;
case 'upload-images':
echo json_encode($tsUpload->newUpload(1));
break;
}
Eso es todo...
Esto le va a gustar ya que vamos a simplificar un poco más lo que sería la sección de cuenta, y será bastante largo, y en mi caso le he quitado el porcentaje que se muestra en cuenta y varias opciones que ningún usuario se toma el tiempo en completar, como por ejemplo "Intereses y preferencias"
1 - En .htaccess buscamos
RewriteRule ^cuenta.php$ inc/php/cuenta.php [QSA,L]y debajo agregamos
RewriteRule ^cuenta/([A-Za-z0-9_-]+)$ inc/php/cuenta.php?accion=$1 [QSA,L]2 - En tema/templates/t.cuenta.tpl y reemplazamos el contenido por esto
{include file='sections/main_header.tpl'}
<script src="{$tsConfig.js}/cuenta.js"></script>
<script>
$(document).ready(() => {
avatar.uid = '{$tsUser->uid}';
avatar.current = '{$tsConfig.url}/files/avatar/{if $tsPerfil.p_avatar}{$tsPerfil.user_id}{else}avatar{/if}.jpg';
});
</script>
<div class="tabbed-d">
<div class="floatL">
<div id="alerta_guarda"></div>
<ul class="menu-tab">
<li{if $tsAccion == ''} class="active"{/if}><a href="{$tsConfig.url}/cuenta/">Cuenta</a></li>
<li{if $tsAccion == 'perfil'} class="active"{/if}><a href="{$tsConfig.url}/cuenta/perfil">Perfil</a></li>
<li{if $tsAccion == 'block'} class="active"{/if}><a href="{$tsConfig.url}/cuenta/block">Bloqueados</a></li>
<li{if $tsAccion == 'clave'} class="active"{/if}><a href="{$tsConfig.url}/cuenta/clave">Cambiar Clave</a></li>
<li{if $tsAccion == 'nick'} class="active"{/if}><a href="{$tsConfig.url}/cuenta/nick">Cambiar Nick</a></li>
<li{if $tsAccion == 'config'} class="active"{/if}><a href="{$tsConfig.url}/cuenta/config">Privacidad</a></li>
</ul>
<a name="alert-cuenta"></a>
<form class="horizontal" method="post" name="editarcuenta">
<input type="hidden" name="pagina" value="{$tsAccion}">
{include file="modules/m.cuenta_$tsAccion.tpl"}
</form>
</div>
<div class="floatR">
{include file='modules/m.cuenta_sidebar.tpl'}
</div>
</div>
<div style="clear:both"></div>
{include file='sections/main_footer.tpl'}3 - Luego acceden a tema/templates/modules y buscan todos los m.cuenta_*.tpl y le quitan (al inicio del archivo)
style="display:none"también (7 el numero puede variar, dependiendo de la sección)
<div class="alert-cuenta cuenta-7"></div>y luego en los divs con la clase .buttons lo reemplazan por
<div class="buttons">
<input type="button" value="Guardar" onclick="cuenta.guardar_datos()" class="mBtn btnOk">
</div>4 - En inc/php/cuenta.php buscan
} elseif($action == 'desactivate'){
if(!empty($_POST['validar'])) echo $tsCuenta->desCuenta();
}
y debajo agregan
$smarty->assign("tsAccion", $_GET["accion"]);
5 - Luego inc/class/c.cuenta.php en la funcion "savePerfil()" cambian
$save = $_POST['save'];
$save = $_POST['pagina'];
dentro del switch hacen lo siguiente, cambian el número por el nombre
- case 1 -> case ''
- case 2 -> case 'perfil'
- case 6 -> case 'clave'
- case 7 -> case 'config'
- case 8 -> case 'nick'
luego eliminan estos
- case 3: ...todo hasta el break... break;
- case 4: ...todo hasta el break... break;
- case 5: ...todo hasta el break... break;
más abajo buscamos y borramos
// COMPROBAR PORCENTAJE
$total = array(5,8,9,8,9); // CAMPOS EN CADA CATEGORIA
$tid = $save - 1;
if($save > 1 && $save < 6){
$total[$tid] = $this->getPorcentTotal($perfilData, $total[$tid]);
if($save == 1) $total[$tid] = $total[$tid] - 2;
$porcen = db_exec('fetch_assoc', db_exec(array(__FILE__, __LINE__), 'query', 'SELECT p_total FROM u_perfil WHERE user_id = \''.$tsUser->uid.'\' LIMIT 1'));
$porcen = unserialize($porcen['p_total']);
$porcen[$tid] = $total[$tid];
$porcenNow = $this->getPorcentVal($porcen);
$porcen = serialize($porcen);
db_exec(array(__FILE__, __LINE__), 'query', 'UPDATE u_perfil SET p_total = \''.$porcen.'\' WHERE user_id = \''.$tsUser->uid.'\'');
}
justo debajo de eso seleccionan
// ACTUALIZAR
if($save == 1) {
db_exec(array(__FILE__, __LINE__), 'query', 'UPDATE u_miembros SET user_email = \''.$tsCore->setSecure($perfilData['email'], true).'\' WHERE user_id = \''.$tsUser->uid.'\'');
array_splice($perfilData, 0, 1); // HACK
$updates = $tsCore->getIUP($perfilData, 'user_');
if(!db_exec(array(__FILE__, __LINE__), 'query', 'UPDATE u_perfil SET '.$updates.' WHERE user_id = \''.$tsUser->uid.'\'')) return array('error' => show_error('Error al ejecutar la consulta de la línea '.__LINE__.' de '.__FILE__.'.', 'db'));
} else {
$updates = $tsCore->getIUP($perfilData, 'p_');
if(!db_exec(array(__FILE__, __LINE__), 'query', 'UPDATE u_perfil SET '.$updates.' WHERE user_id = \''.$tsUser->uid.'\'')) return array('error' => show_error('Error al ejecutar la consulta de la línea '.__LINE__.' de '.__FILE__.'.', 'db'));
}
//
if(is_array($msg_return)) return $msg_return;
else return array('porc' => $porcenNow);
y lo reemplazan por
// ACTUALIZAR
if($save == '' or $save == 'perfil' or $save == 'config') {
if($save == '') {
db_exec([__FILE__, __LINE__], "query", "UPDATE u_miembros SET user_email = '{$perfilData['email']}' WHERE user_id = " . $tsUser->uid);
array_splice($perfilData, 0, 1);
}
$updates = $tsCore->getIUP($perfilData, ($save == '' ? 'user_' : 'p_'));
$msg_return = (db_exec([__FILE__, __LINE__], "query", "UPDATE u_perfil SET {$updates} WHERE user_id = " . $tsUser->uid)) ? array("error" => "Los cambios fueron aceptados y serán aplicados.") : die(show_error('Error al ejecutar la consulta de la línea '.__LINE__.' de '.__FILE__.'.', 'Base de datos'));
}
//
return $msg_return;
6 - Crean un archivo llamado ajax.cuenta.php en inc/php/ajax y agregan lo siguiente
<?php if ( ! defined('TS_HEADER')) exit('No se permite el acceso directo al script');
/**
* Controlador AJAX
*
* @name ajax.cuenta.php
* @author Miguel92
*/
$files = [
'cuenta-guardar' => ['n' => 2, 'p' => ''],
];
// REDEFINIR VARIABLES
$tsPage = 'ajax/p.cuenta.'.$files[$action]['p'];
$tsLevel = $files[$action]['n'];
$tsAjax = empty($files[$action]['p']) ? 1 : 0;
// DEPENDE EL NIVEL
$tsLevelMsg = $tsCore->setLevel($tsLevel, true);
if($tsLevelMsg != 1):
echo '0: '.$tsLevelMsg['mensaje'];
die();
endif;
// CLASE
require("../class/c.cuenta.php");
$tsCuenta = new tsCuenta();
// CODIGO
switch($action){
case 'cuenta-guardar':
echo json_encode($tsCuenta->savePerfil());
break;
}
7 - En tema/templates/modules/m.cuenta_cuenta.tpl lo renombran a m.cuenta_.tpl
8 - En tema/templates/modules/m.cuenta_perfil.tpl y reemplazan el contenido por esto, y aplica este Redes sociales en perfil [Mejorado][Simplificado]
<div class="content-tabs perfil">
<fieldset>
<div class="field">
<label for="nombrez">Nombre completo</label>
<input type="text" value="{$tsPerfil.p_nombre}" maxlength="60" name="nombrez" id="nombrez" class="text cuenta-save-2" style="width:230px">
</div>
<div class="field">
<label for="sitio">Mensaje Personal</label>
<textarea value="" maxlength="60" name="mensaje" id="mensaje" class="cuenta-save-2">{$tsPerfil.p_mensaje}</textarea>
</div>
<div class="field">
<label for="sitio">Sitio Web</label>
<input type="text" value="{$tsPerfil.p_sitio}" maxlength="60" name="sitio" id="sitio" class="text cuenta-save-2" style="width:230px">
</div>
<div class="field">
<label for="red">Redes sociales</label>
<div style="display:grid;grid-template-columns: repeat(2, 1fr);gap: 10px;">
{foreach $tsPerfil.redes key=name item=red}
<div style="display:flex;justify-content: flex-start;align-items: center;">
<div class="icon">
<img src="{$tsConfig.images}/icons/{$name}.png" width="16" height="16" />
</div>
<input type="text" class="text cuenta-save-2" value="{$tsPerfil.p_socials.$name}" placeholder="{$red}" name="red[{$name}]">
</div>
{/foreach}
</div>
</div>
<div class="field">
<label>Me gustaría</label>
<div class="input-fake">
<ul>
{foreach from=$tsPData.gustos key=val item=text}
<li><input type="checkbox" name="g_{$val}" class="cuenta-save-2" value="1"{if $tsPerfil.p_gustos.$val == 1} checked{/if}>{$text}</li>
{/foreach}
</ul>
</div>
</div>
<div class="field">
<label for="estado">Estado Civil</label>
<div class="input-fake">
<select class="cuenta-save-2" name="estado" id="estado">
{foreach from=$tsPData.estado key=val item=text}
<option value="{$val}"{if $tsPerfil.p_estado == $val} selected{/if}>{$text}</option>
{/foreach}
</select>
</div>
</div>
<div class="buttons">
<input type="button" value="Guardar" onclick="cuenta.guardar_datos()" class="mBtn btnOk">
</div>
</fieldset>
<div class="clearfix"></div>
</div>
9 - Ahora se viene lo genial, busca en tema/js/cuenta.js , justo antes que empieza var avatar = {
var cuenta = {... HASTA ... }y lo reemplazas por
var cuenta = {
alerta: (alerta) => {
$(".alert-cuenta").show();
$("#alerta_guarda").html(`<div style="background:#FFFFCC;text-align:center;margin-bottom: 10px;"><p style="display: block;font-size: 16px;padding: 10px 0;">${alerta}</p></div>`)
window.scrollTo(0, 0)
// Despues de 5s quitamos el alerta
setTimeout(() => $("#alerta_guarda").html(''), 5000)
},
chgpais: () => {
// Campo pais
const pais = $("select[name=pais]").val();
const estado = $("select[name=estado]");
if(empty(pais)) estado.addClass('disabled').attr('disabled', 'disabled').val('');
else {
//Obtengo las estados
$(estado).html('');
$('#loading').fadeIn(250);
$.get(global_data.url + '/registro-geo.php', 'pais_code=' + pais, h => {
if(h.charAt(0) === '1') estado.append(h.substring(3)).removeAttr('disabled').val('').focus();
$('#loading').fadeOut(250);
})
}
},
guardar_datos: () => {
$('#loading').slideDown(250);
$.ajax({
type: 'post',
url: global_data.url + '/cuenta-guardar.php',
data: $("form[name=editarcuenta]").serialize(),
dataType: 'json',
success: response => cuenta.alerta(response.error)
});
}
}Eso sería todo, espero no olvidarme de ningún paso.
Mejorado y simplificado
Nueva versión.
En inc/ext/datos.php al final agregar esto
$redes = [
'facebook' => [
'iconify' => 'devicon:facebook',
'nombre' => 'Facebook',
'url' => 'Registrate o inicia tu sesión para ver este contenido'
],
'twitter' => [
'iconify' => 'devicon:twitter',
'nombre' => 'Twitter',
'url' => 'Registrate o inicia tu sesión para ver este contenido'
],
'instagram' => [
'iconify' => 'skill-icons:instagram',
'nombre' => 'Instagram',
'url' => 'Registrate o inicia tu sesión para ver este contenido'
],
'youtube' => [
'iconify' => 'logos:youtube-icon',
'nombre' => 'Youtube',
'url' => 'Registrate o inicia tu sesión para ver este contenido'
],
'twitch' => [
'iconify' => 'logos:twitch',
'nombre' => 'Twitch',
'url' => 'Registrate o inicia tu sesión para ver este contenido'
],
'tiktok' => [
'iconify' => 'logos:tiktok-icon',
'nombre' => 'Tiktok',
'url' => 'Registrate o inicia tu sesión para ver este contenido@'
],
'discord' => [
'iconify' => 'skill-icons:discord',
'nombre' => 'Discord',
'url' => 'Registrate o inicia tu sesión para ver este contenido
],
'reddit' => [
'iconify' => 'logos:reddit-icon',
'nombre' => 'Reddit',
'url' => 'Registrate o inicia tu sesión para ver este contenido
]
];
En inc/class/c.cuenta.php en la funcion loadPerfil() debajo de
$perfilInfo = $this->unData($perfilInfo);
Agregan
// Redes viculadas
$perfilInfo['socials'] = empty($perfilInfo['user_socials']) ? '' : json_decode($perfilInfo['user_socials'], true);
En la función unData() buscar
$data['p_socials'] = unserialize($data['p_socials']);
$data['p_socials']['f'] = $data['p_socials'][0];
$data['p_socials']['t'] = $data['p_socials'][1];
y reemplazar
// Redes sociales
$data["redes"] = $redes;
$data['p_socials'] = ($data['p_socials'] != NULL) ? json_decode($data['p_socials'], true) : [];
foreach ($redes as $name => $valor) $data['p_socials'][$name];
En la función loadHeadInfo() buscar
$data['p_socials'] = unserialize($data['p_socials']);
$data['p_socials']['f'] = $data['p_socials'][0];
$data['p_socials']['t'] = $data['p_socials'][1];
y lo reemplazan por
// Redes Sociales
$data['p_socials'] = '';
if(!empty($data['p_socials'])) {
$data['p_socials'] = json_decode($data['p_socials'], true);
foreach ($redes as $name => $valor) $data['p_socials'][$name];
}
En la función savePerfil() buscar
// EXTERNAS
$facebook = $tsCore->setSecure($tsCore->parseBadWords($_POST['facebook']), true);
$twitter = $tsCore->setSecure($tsCore->parseBadWords($_POST['twitter']), true);
y lo reemplazan por
// EXTERNAS, Redes sociales
$red__social = [];
foreach ($_POST["red"] as $llave => $id) $red__social[$llave] = $tsCore->setSecure($tsCore->parseBadWords($id), true);
luego buscan
'socials' => serialize(array($facebook,$twitter)),
y lo cambian por
'socials' => json_encode($red__social),
En inc/php/cuenta.php buscan
$smarty->assign("tsPerfil", $tsPerfil);
debajo agregan
$smarty->assign("tsRedes", $redes);
En inc/php/perfil.php buscan
$smarty->assign("tsInfo", $tsInfo);
y debajo agregan
$smarty->assign("tsRedes", $redes);
En tema/templates/modules/m.cuenta_perfil_me.tpl buscan
<div class="field">
<label for="ft">Redes sociales</label>
<img src="{$tsConfig.default}/images/icons/facebook.png" width="16" height="16" style="margin:5px; float:left" />
<strong>facebook.com/</strong><input type="text" value="{$tsPerfil.p_socials.f}" maxlength="64" name="facebook" id="ft" class="text cuenta-save-2" style="width:204px"><br />
<img src="{$tsConfig.default}/images/icons/twitter.png" width="16" height="16" style="margin:8px 5px 5px 160px; float:left" />
<strong>twitter.com/</strong><input type="text" value="{$tsPerfil.p_socials.t}" maxlength="64" name="twitter" id="ft2" class="text cuenta-save-2" style="margin-top:3px; width:204px"><br />
</div>y lo reemplazan por
<div class="field">
<label for="ft">Redes sociales</label>
<div style="display:grid;gap:.3rem;grid-template-columns: repeat(2, 1fr);">
{foreach from=$tsRedes key=name item=red}
<div class="red-item">
<iconify-icon icon="{$red.iconify}"></iconify-icon>
<input type="text" class="text" value="{$tsPerfil.p_socials.$name}" placeholder="{$red.nombre}" name="red[{$name}]">
</div>
{/foreach}
</div>
</div>En tema/templates/modules/m.perfil_sidebar.tpl y arriba de (En realidad lo pueden poner donde ustedes quieran)
<div style="margin-bottom: 10px">
{$tsConfig.ads_300}
</div>agregan
{if $tsInfo.p_socials != ''}
<div class="widget w-seguidores clearfix">
<div class="title-w clearfix">
<h3>Redes Sociales</h3>
</div>
<div>
{assign var="redesConContenido" value=[]}
{assign var="redesSinContenido" value=[]}
{foreach $tsRedes key=name item=red}
{if $tsInfo.p_socials.$name != ''}
{assign var="redConContenido" value="<a class='sitio icon icon_$name' target='_blank' href='{$red.url}/{$tsInfo.p_socials.$name}' title='{$red.nombre}' class=''></a>"}
{append var="redesConContenido" value=$redConContenido}
{else}
{assign var="redSinContenido" value="<span class='sitio icon icon_$name icon_off'></span>"}
{append var="redesSinContenido" value=$redSinContenido}
{/if}
{/foreach}
{foreach from=$redesConContenido as $redConContenidoItem}
{$redConContenidoItem}
{/foreach}
{foreach from=$redesSinContenido as $redSinContenidoItem}
{$redSinContenidoItem}
{/foreach}
</div>
<br>
{/if}En perfil.css agregan
.icon {
display: inline-block;
width: 2rem;
height: 2rem;
margin: 0.3rem;
}
.icon.icon_off {
opacity: .5;
}
.icon.icon_facebook {
background: url('Registrate o inicia tu sesión para ver este contenido no-repeat center center / contain;
}
.icon.icon_twitter {
background: url('Registrate o inicia tu sesión para ver este contenido no-repeat center center / contain;
}
.icon.icon_instagram {
background: url('Registrate o inicia tu sesión para ver este contenido no-repeat center center / contain;
}
.icon.icon_youtube {
background: url('Registrate o inicia tu sesión para ver este contenido no-repeat center center / contain;
}
.icon.icon_twitch {
background: url('Registrate o inicia tu sesión para ver este contenido no-repeat center center / contain;
}
.icon.icon_tiktok {
background: url('Registrate o inicia tu sesión para ver este contenido no-repeat center center / contain;
}
.icon.icon_discord {
background: url('Registrate o inicia tu sesión para ver este contenido no-repeat center center / contain;
}
.icon.icon_reddit {
background: url('Registrate o inicia tu sesión para ver este contenido no-repeat center center / contain;
}Por último tema/templates/t.cuenta.tpl arriba de {include file="sections/main_footer.tpl"} agregan
<script src="Registrate o inicia tu sesión para ver este contenido"></script>
En cuenta
[img]Registrate o inicia tu sesión para ver este contenido[/img]
En perfil (los de poco color es que no tienen enlaces y no tienen enlace, en cuando vayan agregando los enlaces se verá el color completo)
[img]Registrate o inicia tu sesión para ver este contenido[/img]
Capturas
Nota: El tutorial es por si quieren añadir otra red social, si suben solo los archivos y hacen solo el paso del include y el css ya tienen las medallas automáticas instaladas.
Ejemplo añadir cuenta red social
En c.cuenta.php --> inc --> class --> c.cuenta.php
Buscar:
$data['p_socials']['t'] = $data['p_socials'][1];
Debajo agregar:
$data['p_socials']['yt'] = $data['p_socials'][2];
$data['p_socials']['i'] = $data['p_socials'][3];
Buscar:
$data['p_nombre'] = $tsCore->setSecure($tsCore->parseBadWords($data['p_nombre']), true);
$data['p_mensaje'] = $tsCore->setSecure($tsCore->parseBadWords($data['p_mensaje']), true);
$data['p_socials'] = unserialize($data['p_socials']);
$data['p_socials']['f'] = $data['p_socials'][0];
$data['p_socials']['t'] = $data['p_socials'][1];
Debajo agregar:
$data['p_socials']['yt'] = $data['p_socials'][2];
$data['p_socials']['i'] = $data['p_socials'][3];
Buscar:
$facebook = $tsCore->setSecure($tsCore->parseBadWords($_POST['facebook']), true);
$twitter = $tsCore->setSecure($tsCore->parseBadWords($_POST['twitter']), true);
Debajo agregar:
$youtuber = $tsCore->setSecure($tsCore->parseBadWords($_POST['youtuber']), true);
$instagram = $tsCore->setSecure($tsCore->parseBadWords($_POST['instagram']), true);
Buscar:
'socials' => serialize(array($facebook,$twitter)),
Después de:
,$twitter
Agregar:
,$youtuber,$instagram
'socials' => serialize(array($facebook,$twitter,$youtuber,$instagram)),
En m.cuenta_perfil_me.tpl --> themes --> default --> templates --> modules --> m.cuenta_perfil_me.tpl
Buscar:
<img src="{$tsConfig.default}/images/icons/twitter.png" width="16" height="16" style="margin:8px 5px 5px 160px; float:left" />
<strong>twitter.com/</strong><input type="text" value="{$tsPerfil.p_socials.t}" maxlength="64" name="twitter" id="ft2" class="text cuenta-save-2" style="margin-top:3px; width:204px"><br />
Debajo agregar:
<img src="{$tsConfig.default}/images/icons/icoyoutube.png" width="16" height="16" style="margin:8px 5px 5px 160px; float:left" />
<strong>youtube.com/</strong><input type="text" value="{$tsPerfil.p_socials.yt}" maxlength="64" name="youtuber" id="ft2" class="text cuenta-save-2" style="margin-top:3px; width:204px"><br />
<img src="{$tsConfig.default}/images/icons/instagram.png" width="16" height="16" style="margin:8px 5px 5px 160px; float:left" />
<strong>instagram.com/</strong><input type="text" value="{$tsPerfil.p_socials.i}" maxlength="64" name="instagram" id="ft2" class="text cuenta-save-2" style="margin-top:3px; width:204px"><br />
En m.perfil_headinfo.tpl --> themes --> default --> templates --> modules --> m.perfil_headinfo.tpl
Buscar:
{if $tsInfo.p_socials.f}
<li style="float:right!important;" class="floatR">
<a target="_blank" href="Registrate o inicia tu sesión para ver este contenido{$tsInfo.p_socials.f}" title="Facebook"><img height="14" width="14" src="{$tsConfig.default}/images/icons/facebook.png"/></a>
</li>
{/if}
Debajo agregar:
{if $tsInfo.p_socials.yt}
<li style="float:right!important;" class="floatR">
<a target="_blank" href="Registrate o inicia tu sesión para ver este contenido{$tsInfo.p_socials.yt}" title="Canal de YouTube"><img height="14" width="14" src="{$tsConfig.default}/images/icons/icoyoutube.png"/></a>
</li>
{/if}
{if $tsInfo.p_socials.i}
<li style="float:right!important;" class="floatR">
<a target="_blank" href="Registrate o inicia tu sesión para ver este contenido{$tsInfo.p_socials.i}" title="Canal de Instagram"><img height="14" width="14" src="{$tsConfig.default}/images/icons/instagram.png"/></a>
</li>
{/if}
En m.perfil_sidebar.tpl --> themes --> default --> templates --> modules --> m.perfil_sidebar.tpl
Buscar:
<div class="widget w-medallas clearfix">
<div class="title-w clearfix">
<h3>Medallas</h3>
<span>{$tsGeneral.m_total}</span>
</div>
{if $tsGeneral.m_total}
<ul class="clearfix">
{foreach from=$tsGeneral.medallas item=m}
<img src="{$tsConfig.tema.t_url}/images/icons/med/{$m.m_image}_16.png" class="qtip" title="{$m.m_title} - {$m.m_description}"/>
{/foreach}
</ul>
{if $tsGeneral.m_total >= 21}<a href="#medallas" onclick="perfil.load_tab('medallas', $('#medallas'));" class="see-more">Ver más »</a>{/if}
{else}
<div class="emptyData">No tiene medallas</div>
{/if}
</div>
Reemplazar por:
{include file='modules/m.perfil_medallas.tpl'}
En estilo.css --> themes --> default --> estilo.css
Al final agregar:
.list-element .remove a{display:block!important;width:16px!important;height:16px!important}.list-element .medalla{margin-right:10px;display:block;width:32px;height:32px;float:left}.list-element .link-medalla{float:none;width:auto!important;margin-right:5px}.w-medallas ul li{margin:0 8px 8px 0;direction:block;float:left}span.icon-medallas{width:33px;height:33px;display:block;background:transparent url('images/medallas32.png') no-repeat bottom right}span.icon-medallas.Desarrollador{background-position:-70px -375px}span.icon-medallas.Fundador{background-position:-69px -409px}span.icon-medallas.Disenador{background-position:-136px -579px}span.icon-medallas.Administrador{background-position:-103px -579px}span.icon-medallas.Moderador{background-position:-69px -579px}span.icon-medallas.Full.User{background-position:-34px -579px}span.icon-medallas.Great.User{background-position:-34px -579px}span.icon-medallas.Silver.User{background-position:-34px -579px}span.icon-medallas.Gold.User{background-position:-34px -579px}span.icon-medallas.Oficial{background-position:-308px -***** -511px}span.icon-medallas.staff{background-position:-70px -341px}span.icon-medallas.c-bronce{background-position:0 -69px}span.icon-medallas.c-plata{background-position:-35px -69px}span.icon-medallas.c-oro{background-position:-69px -69px}span.icon-medallas.c-ruby{background-position:-103px -69px}span.icon-medallas.c-esmeralda{background-position:-137px -69px}span.icon-medallas.c-zafiro{background-position:-171px -69px}span.icon-medallas.c-cobalto{background-position:-205px -69px}span.icon-medallas.c-platino{background-position:-239px -69px}span.icon-medallas.c-diamante{background-position:-273px -69px}span.icon-medallas.p-bronce{background-position:0 0}span.icon-medallas.p-plata{background-position:-34px 0}span.icon-medallas.p-oro{background-position:-69px 0}span.icon-medallas.p-ruby{background-position:-103px 0}span.icon-medallas.p-esmeralda{background-position:-137px 0}span.icon-medallas.p-zafiro{background-position:-172px 0}span.icon-medallas.p-cobre{background-position:-205px 0}span.icon-medallas.p-cobalto{background-position:-240px 0}span.icon-medallas.p-platino{background-position:-273px 0}span.icon-medallas.p-diamante{background-position:-307px 0}span.icon-medallas.s-bronce{background-position:0 -171px}span.icon-medallas.s-plata{background-position:-35px -171px}span.icon-medallas.s-oro{background-position:-69px -171px}span.icon-medallas.s-platino{background-position:-240px -171px}span.icon-medallas.pt-bronce{background-position:0 -103px}span.icon-medallas.pt-plata{background-position:-35px -103px}span.icon-medallas.pt-oro{background-position:-69px -103px}span.icon-medallas.pt-ruby{background-position:-103px -103px}span.icon-medallas.pt-esmeralda{background-position:-137px -103px}span.icon-medallas.pt-zafiro{background-position:-172px -103px}span.icon-medallas.pt-cobalto{background-position:-205px -103px}span.icon-medallas.pt-platino{background-position:-240px -103px}span.icon-medallas.pt-diamante{background-position:-273px -103px}span.icon-medallas.k-bronce{background-position:0 -273px}span.icon-medallas.k-plata{background-position:-35px -273px}span.icon-medallas.k-oro{background-position:-69px -273px}span.icon-medallas_toup{width:33px;height:33px;display:block;background:transparent url('images/adsense.png') no-repeat bottom right}span.icon-medallas.Novato{background-position:-204px -579px}span.icon-medallas.Full{background-position:-272px -579px}span.icon-medallas.Full_User{background-position:-238px -579px}span.icon-medallas.Great{background-position:-34px -579px}span.icon-medallas.programador{background-position:-308px -579px}
En phpost.css --> themes --> default --> phpost.css
Al final agregar:
.med-alt.med-facebook{background:url("images/icons/med/face_32.png") no-repeat;display:block;margin-right:7px;width:34px;height:33px;float:left}.med-alt.med-twitter{background:url("images/icons/med/twit_32.png") no-repeat;display:block;margin-right:7px;width:34px;height:33px;float:left}.med-alt.med-youtube{background:url('images/icons/med/youtube_32.png');display:block;margin-right:7px;width:34px;height:33px;float:left}
Y subir esta carpeta:
Mega
http://Registrate o inicia tu sesión par... contenido
Google Drive
http://Registrate o inicia tu sesión par... contenido
Mediafire
http://Registrate o inicia tu sesión par... contenido
Uptobox
http://Registrate o inicia tu sesión par... contenido
4shared
http://Registrate o inicia tu sesión par... contenido
Creditos: Kmario19
Esto es básicamente para el administrador principal con el user_id 1, para evitar que otro administrador meta mano.
1 - En inc/class/c.admin.php buscamos
/*
saveConfigs()
*/
function saveConfig() {
y arriba agregaremos (Actualizado 25.02.22)
# Ejecutamos las consultas!
public function executeSQL() {
global $tsCore, $tsUser;
# Solo administrador principal
if($tsUser->is_member AND $tsUser->is_admod === 1 AND intval($tsUser->uid) === 1) {
# Cosultas
$sqlList = [];
$lines = explode("\n", $_POST['sql']);
foreach($lines as $sql) {
$sql = htmlspecialchars_decode($sql);
$sql = str_replace("'", '"', $sql);
# Quitamos ; solo si lo tiene
if(preg_match("/(.*);/", $sql)) $sql = substr(trim($sql), 0, strlen($sql) - 1);
array_push($sqlList, db_exec([__FILE__, __LINE__], 'query', $sql));
}
return in_array(true, $sqlList) ? '1: Se ejecutó correctamente.' : '0: Hubo un error al ejecutar la/s sentencia/s';
} return '0: Solo el administrador principal puede.';
}
2 - En inc/php/ajax/ajax.admin.php buscamos (Actualizado 25.02.22)
'admin-badwords-delete' => array('n' => 4, 'p' => ''),
debajo pegamos
'admin-ejecutar-sql' => array('n' => 4, 'p' => ''),
más abajo buscamos
default:
die('0: Este archivo no existe.');
break;
y arriba pegamos
case 'admin-ejecutar-sql':
echo $tsAdmin->executeSQL();
break;
3 - En inc/php/admin.php si tienen agregado esto, bórrenlo, ya no se usará (Actualizado 25.02.22)
} elseif($action == 'execute') {
if($tsAdmin->executeSQL()) $tsCore->redirectTo($url);
else $smarty->assign("tsError", $tsAdmin->executeSQL());
4 - Luego en tema/templates/t.admin.tpl y buscamos
{elseif $tsAction == 'configs'}
{include file='admin_mods/m.admin_configs.tpl'}y debajo pegamos
{elseif $tsAction == 'execute'}
{include file='admin_mods/m.admin_execute.tpl'}5 - En tema/templates/admin_mods/m.admin_sidemenu.tpl buscamos
<h4>Configuración de PHPost</h4>y arriba pegamos
<li id="a_execute"><span class="cat-title"><a href="{$tsConfig.url}/admin/execute">Ejecutar consultas </a></span></li>6 - En tema/templates/admin_mods/ crearemos un archivo llamado m.admin_execute.tpl y agregaremos esto (Actualizado 25.02.22)
<div class="boxy-title">
<h3>Administrar Base de datos</h3>
</div>
<div id="res" class="boxy-content">
{if $tsSave}<div class="alerts ok">Tus cambios han sido guardados.</div>{/if}
{if $tsError}<div class="alerts error">Hubo problemas al ejecutar las sentencias.</div>{/if}
<h4>Desde aquí tu puedes <u>generar las consultas</u>, antes de hacerlo, comprueba de que este correctamente.</h4>
<form method="post" autocomplete="off">
<legend>Consultas</legend>
<textarea name="sql" id="sql" cols="30" rows="10" placeholder="EJ: ALTER TABLE w_configuracion ADD privado INT(11) NOT DEFAULT 0;"></textarea>
<small style="display:block"><i>Siempre cada consulta debe terminar en ; (punto y coma)</i></small>
<input type="button" onclick="database.execute();" value="Enviar consulta" class="mBtn btnOk"/>
</form>
</div>7 - En tema/js/admin.js al final del archivo agregan (Actualizado 25.02.22)
/**
* Database
* para ejecutar consultas y crear copias
*/
var database = new function() {
this.execute = () => {
$.post(global_data.url + '/admin-ejecutar-sql.php', 'sql=' + $("#sql").val(), h => {
switch (h.charAt(0)) {
case '0':
mydialog.alert('Error', h.substring(3), false)
break;
case '1':
mydialog.alert('Bien', h.substring(3), true)
break;
}
})
}
}Eso sería todo, pero úsenlo con mucho cuidado ya que podrían afectar la base de datos, ejemplo quieren eliminar a un usuario y si colocan su id los eliminará a ustedes..
NOTA:
Estoy creando algo que incluye esta función, lo cual ustedes van a poder ejecutar consultas, crear copias de seguridad de su base de datos y descargar dicha copia, pero debo averiguar un poco más...
Hola me presento soy Kreatore , y estoy aquí para hacer mi web y aprender a manejarlo bien tengo 23 años y de pequeñito tambien usaba phpost. Un saludo a todos ;D
Bueno acá les traigo una solución, pero deben tener en cuenta que los campos en la administración específicamente "Configuración" en el atributo name debe coincidir con la que tienen en la base de datos, ya que si no son iguales, este les devolverá un error.
Ejemplo 1:
Cita:<input type="text" id="ai_titulo" name="titulo" maxlength="24" value="{$tsConfig.titulo}" />
El titulo coincide con w_configuracion.titulo de la base de datos
Cita:<textarea name="message_welcome" id="ai_met_welcome" style="width: 260px; height: 100px; {if $tsConfig.c_met_welcome == 0} display:none; {/if}">{$tsConfig.c_message_welcome}</textarea>
El message_welcome no coincide con w_configuracion.c_message_welcome de la base de datos ya que este comienza con c_ o tal vez comienzan con c_allow_.
Les dejó el archivo m.admin_configs.tpl para los que no quieran buscar, cabe recalcar que si tienen campos agregados lo tendrán que agregar devuelta. (Es de la versión default)
Bueno una vez aclarado esto, continuamos, buscamos en el archivo ya mencionado inc/class/c.admin.php...
function saveConfig()
{
global $tsCore;
//
$c = array(
'titulo' => $tsCore->setSecure($tsCore->parseBadWords($_POST['titulo'])),
'slogan' => $tsCore->setSecure($tsCore->parseBadWords($_POST['slogan'])),
'url' => $tsCore->setSecure($tsCore->parseBadWords($_POST['url'])),
'offline' => empty($_POST['offline']) ? 0 : 1,
'offline_message' => $tsCore->setSecure($tsCore->parseBadWords($_POST['offline_message'])),
'chat' => $tsCore->setSecure($_POST['chat']),
'xat' => $tsCore->setSecure($_POST['xat']),
'edad' => $tsCore->setSecure($_POST['edad']),
'active' => $tsCore->setSecure($_POST['active']),
'sess_ip' => empty($_POST['sess_ip']) ? 0 : 1,
'count_guests' => $tsCore->setSecure($_POST['count_guests']),
'reg_active' => empty($_POST['reg_active']) ? 0 : 1,
'reg_activate' => empty($_POST['reg_activate']) ? 0 : 1,
'met_welcome' => $tsCore->setSecure($_POST['met_welcome']),
'message_welcome' => $tsCore->setSecure($tsCore->parseBadWords($_POST['message_welcome'])),
'fotos_private' => empty($_POST['fotos_private']) ? 0 : 1,
'hits_guest' => empty($_POST['hits_guest']) ? 0 : 1,
'keep_points' => empty($_POST['keep_points']) ? 0 : 1,
'allow_points' => $tsCore->setSecure($_POST['allow_points']),
'see_mod' => empty($_POST['see_mod']) ? 0 : 1,
'stats_cache' => $tsCore->setSecure($_POST['stats_cache']),
'desapprove_post' => empty($_POST['desapprove_post']) ? 0 : 1,
'firma' => empty($_POST['firma']) ? 0 : 1,
'upload' => empty($_POST['upload']) ? 0 : 1,
'portal' => empty($_POST['portal']) ? 0 : 1,
'live' => empty($_POST['live']) ? 0 : 1,
'max_nots' => $tsCore->setSecure($_POST['max_nots']),
'max_acts' => $_POST['max_acts'],
'max_posts' => $tsCore->setSecure($_POST['max_posts']),
'max_com' => $tsCore->setSecure($_POST['max_com']),
'sump' => empty($_POST['sump']) ? 0 : 1,
'newr' => empty($_POST['newr']) ? 0 : 1,
'pkey' => $tsCore->setSecure($_POST['pkey']),
'skey' => $tsCore->setSecure($_POST['skey']),
);
// UPDATE
if (db_exec(array(__FILE__, __LINE__), 'query', 'UPDATE `w_configuracion` SET `titulo` = \'' . $c['titulo'] . '\', `slogan` = \'' .
$c['slogan'] . '\', `url` = \'' . $c['url'] . '\', `chat_id` = \'' . $c['chat'] .
'\', `xat_id` = \'' . $c['xat'] . '\',`c_last_active` = \'' . $c['active'] . '\', `c_allow_sess_ip` = \'' .
$c['sess_ip'] . '\', `c_count_guests` = \'' . $c['count_guests'] . '\', `c_reg_active` = \'' .
$c['reg_active'] . '\', `c_reg_activate` = \'' . $c['reg_activate'] . '\', `c_met_welcome` = \'' .
$c['met_welcome'] . '\', `c_message_welcome` = \'' . $c['message_welcome'] . '\', `c_fotos_private` = \'' .
$c['fotos_private'] . '\', `c_hits_guest` = \'' . $c['hits_guest'] . '\', `c_keep_points` = \'' .
$c['keep_points'] . '\', `c_allow_points` = \'' . $c['allow_points'] . '\', `c_see_mod` = \'' .
$c['see_mod'] . '\', `c_stats_cache` = \'' . $c['stats_cache'] . '\',`c_desapprove_post` = \'' .
$c['desapprove_post'] . '\', `c_allow_edad` = \'' . $c['edad'] . '\', `c_max_posts` = \'' .
$c['max_posts'] . '\', `c_max_com` = \'' . $c['max_com'] . '\', `c_max_nots` = \'' .
$c['max_nots'] . '\', `c_max_acts` = \'' . $c['max_acts'] . '\', `c_allow_sump` = \'' .
$c['sump'] . '\', `c_newr_type` = \'' . $c['newr'] . '\', `c_allow_firma` = \'' .
$c['firma'] . '\', `c_allow_upload` = \'' . $c['upload'] . '\', `c_allow_portal` = \'' .
$c['portal'] . '\', `c_allow_live` = \'' . $c['live'] . '\', `offline` = \'' . $c['offline'] .
'\', `offline_message` = \'' . $c['offline_message'] . '\', `pkey` = \'' . $c['pkey'] . '\', `skey` = \'' . $c['skey'] . '\' WHERE `tscript_id` = \'1\''))
return true;
else
exit( show_error('Error al ejecutar la consulta de la línea '.__LINE__.' de '.__FILE__.'.', 'db') );
}
y la reemplazamos por
public function saveConfig() {
global $tsCore;
/**
* Unimos todos los parametros y
* quitamos el $_POST["save"] con array_slice()
* @link Registrate o inicia tu sesión para ver este contenido
* con el -1 se quita el $_POST["save"]
*/
$columnas = $tsCore->getIUP( array_slice($_POST, 0, -1) );
if (db_exec([__FILE__, __LINE__], "query", "UPDATE w_configuracion SET {$columnas} WHERE tscript_id = 1")) return true;
else exit( show_error('Error al ejecutar la consulta de la línea '.__LINE__.' de '.__FILE__.'.', 'Base de datos') );
}
Ahora en el caso que tengan que incluir por ejemplo el "Proveedor de Email", ya que ese campo se tiene que codificar a JSON...y pensarán, como lo hago es simple..
Se pregunta si existe el parámetro con dicho nombre como por ejemplo isset($_POST['el_nombre']), como el nombre que necesitamos es 'providers' se hace lo siguiente, arriba de $columnas se agrega la condicional.
# Consultamos si existe, tenemos que poner el nombre
if(isset($_POST["providers"])):
# Lo que va a hacer es reemplazar el parametro por este nuevo
/**
* @link Registrate o inicia tu sesión para ver este contenido
*/
$_POST["providers"] = json_encode(explode(', ', $_POST["providers"]), JSON_FORCE_OBJECT);
endif;
y eso sería básicamente todo, espero haberme explicado bien.
Este "mod" es dual ya que tiene el login y registro, esta realizado de forma que al ingresar a al sitio tengas que registrarte o loguearte de forma obligatoria...
una vista previa de como quedará
[img]Registrate o inicia tu sesión para ver este contenido[/img]
Bueno comencemos
1 - En header.php buscamos
define('TS_TEMA', $tsTema);
y debajo agregamos
# El tipo de inicio
define('INICIO_DUAL', TRUE);
2 - Buscamos en index.php
// Checamos...
if($tsCore->settings['c_allow_portal'] == 1 && $tsUser->is_member == true && $_GET['do'] == 'portal') {
// Portal/mi
include('inc/php/portal.php');
} else {
// Home
include('inc/php/posts.php');
}
y la reemplazamos por
/**
* Dependiendo de la configuración
* Solo aplicará el dual si ambos son "TRUE"
*/
if(INICIO_DUAL AND !$tsUser->is_member) include 'inc/php/dual.php';
else {
// Checamos...
if(intval($tsCore->settings['c_allow_portal']) == 1 && $tsUser->is_member == true && $_GET['do'] == 'portal') {
// Portal/mi
include('inc/php/portal.php');
} else {
// Home
include('inc/php/posts.php');
}
}
3 - Descargan los archivos y los ponen en las rutas correspondientes
* dual.php en inc/php/
* dual.css en tema/css/
* dual.js en tema/js/
* t.dual.tpl en tema/templates/
4 - Ir a inc/class/c.registro.php y buscan
$tsData = array(
'user_nick' => $tsCore->parseBadWords($_POST['nick']),
'user_password' => $tsCore->parseBadWords($_POST['password']),
'user_email' => $_POST['email'],
'user_dia' => $_POST['dia'],
'user_mes' => $_POST['mes'],
'user_anio' => $_POST['anio'],
'user_sexo' => $_POST['sexo'] == 'f' ? '0' : 1,
'user_pais' => strtoupper($_POST['pais']),
'user_estado' => $_POST['estado'],
'user_terminos' => $_POST['terminos'],
'user_captcha' => $_POST['g-recaptcha-response'],
'user_registro' => time(),
);
y la reemplazan por
$tsData = array(
'user_nick' => $tsCore->parseBadWords($_POST['nick']),
'user_password' => $tsCore->parseBadWords($_POST['password']),
'user_email' => $_POST['email'],
'user_sexo' => $_POST['sexo'] == 'f' ? '0' : 1,
'user_terminos' => $_POST['terminos'],
'user_captcha' => $_POST['response'],
'user_registro' => time(),
);
un poco más abajo buscan (Cabe recalcar que deben tener las claves del reCaptcha v3 agregadas)
/** reCAPTCHA **/
$recaptcha = 'Registrate o inicia tu sesión para ver este contenido . $tsCore->settings['skey'] . '&response=' . $tsData['user_captcha'] . '&remoteip=' . $tsCore->getIP();
// Obtener respuesta
$response = file_get_contents($recaptcha);
// Extraer resultado
$ext1 = explode('"success":', $response);
$ext2 = explode(',', $ext1[1]);
// Comprobar resultado
$valid = trim($ext2[0]);
// Devolver respuesta si es incorrecta
if ($valid == 'false') {
return 'recaptcha: No hemos podido validar tu humanidad';
}
y la reemplazan por
/**
* Comprobamos el recaptcha v3
*/
$response = $tsCore->reCaptcha($tsData['user_captcha']);
if (!$response) return 'recaptcha: No hemos podido validar tu humanidad';
más abajo buscamos
// INSERTAMOS EL PERFIL
db_exec(array(__FILE__, __LINE__), 'query', 'INSERT INTO `u_perfil` (`user_id`, `user_dia`, `user_mes`, `user_ano`, `user_pais`, `user_estado`, `user_sexo`) VALUES (\''.(int)$tsData['user_id'].'\', \''.(int)$tsData['user_dia'].'\', \''.(int)$tsData['user_mes'].'\', \''.(int)$tsData['user_anio'].'\', \''.$tsCore->setSecure($tsData['user_pais']).'\', \''.$tsCore->setSecure($tsData['user_estado']).'\', \''.(int)$tsData['user_sexo'].'\')');
db_exec(array(__FILE__, __LINE__), 'query', 'INSERT INTO `u_portal` (`user_id`) VALUES (\''.$tsData['user_id'].'\')');
y la reemplazamos por
db_exec([__FILE__, __LINE__], "query", "INSERT INTO u_perfil (user_id, p_avatar, user_sexo) VALUES({$tsData['user_id']}, 1, {$tsData['user_sexo']})");
db_exec([__FILE__, __LINE__], "query", "INSERT INTO u_portal (user_id) VALUES({$tsData['user_id']})");
# Generamos automaticamente un avatar
$avatar = "Registrate o inicia tu sesión para ver este contenido";
$copy = "../../files/avatar/$2_$3.jpg";
$sizes = [50, 120];
foreach ($sizes as $size) {
copy(
str_replace('$1', $tsData['user_nick'], $avatar),
str_replace(['$2', '$3'], [$tsData['user_id'], $size], $copy)
)
}
5 - Luego en inc/class/c.core.php buscamos
/*
getIP
*/
function getIP(){
y arriba agregamos
# Función para comprobar reCaptcha v3
public function reCaptcha(string $publico = '') {
$http = http_build_query([
'secret' => $this->settings["skey"],
'response' => $publico,
'remoteip' => $this->getIP()
]);
$init = curl_init();
curl_setopt($init, CURLOPT_URL, "Registrate o inicia tu sesión para ver este contenido");
curl_setopt($init, CURLOPT_POST, 1);
curl_setopt($init, CURLOPT_POSTFIELDS, $http);
curl_setopt($init, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($init);
curl_close($init);
return json_decode($response, true);
}
Y eso sería todo, espero no olvidarme de nada!
Cualquier problema o duda comenten!

