Plugin de WordPress para frases aleatorias

Valencia, 29/08/2026, G.B.
Tal y comenté recientemente en este post, aquí va el plugin de WordPress para frases aleatorias. Os paso también el archivo.php.

Recordad que para subir un plugin a WordPress (que no esté en el repositorio, como es el caso), hay que crear una carpeta con el mismo nombre del archivo .php, comprimirlo en .zip y subirlo por FTP o S FTP (mejor esto último) al apartado del escritorio de WordPress «Subir plugin».

Aquí va e código de la primera versión del plugin. Si no consigues verlo correcatente en este post, descarga el archivo frases-aleatorias.zip de mi cuenta de pCloud e instálalo en WordPRess – > Plugins.

<?php
/**
* Plugin Name: Frases Aleatorias Widget
* Description: Widget, shortcode

y panel de administración para gestionar frases (añadir, editar, eliminar). Cada frase incluye enlace de traducción automática al castellano. Sin dependencias externas.
* Version: 2.0
* Author: Puntocomunica.com
*/

if ( ! defined( ‘ABSPATH’ ) ) {
exit; // Salida directa no permitida.
}

define( ‘FA_TABLA’, ‘fa_frases’ ); // se le añadirá el prefijo de WP al usarla

/* ==========================================================
* INSTALACIÓN: crea la tabla y la rellena con las 50 frases
* la primera vez que se activa el plugin.
* ========================================================== */
function fa_activar_plugin() {
global $wpdb;
$tabla = $wpdb->prefix . FA_TABLA;
$charset_collate = $wpdb->get_charset_collate();

$sql = «CREATE TABLE $tabla (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
texto TEXT NOT NULL,
autor VARCHAR(191) NOT NULL,
PRIMARY KEY (id)
) $charset_collate;»;

require_once ABSPATH . ‘wp-admin/includes/upgrade.php’;
dbDelta( $sql );

// Solo se siembran datos si la tabla está vacía (primera instalación)
$total = $wpdb->get_var( «SELECT COUNT(*) FROM $tabla» );
if ( (int) $total === 0 ) {
$frases_iniciales = array(
array( «He who blames others has a long way to go on his journey. He who blames himself is halfway there. He who blames no one has arrived.», «Chinese Proverb» ),
array( «The only way to do great work is to love what you do.», «Steve Jobs» ),
array( «In the middle of difficulty lies opportunity.», «Albert Einstein» ),
array( «It does not matter how slowly you go as long as you do not stop.», «Confucius» ),
array( «Fall seven times, stand up eight.», «Japanese Proverb» ),
array( «The journey of a thousand miles begins with a single step.», «Lao Tzu» ),
array( «Whether you think you can or you think you can’t, you’re right.», «Henry Ford» ),
array( «The best way to predict the future is to create it.», «Abraham Lincoln» ),
array( «Success is not final, failure is not fatal: it is the courage to continue that counts.», «Winston Churchill» ),
array( «Believe you can and you’re halfway there.», «Theodore Roosevelt» ),
array( «Do not go where the path may lead, go instead where there is no path and leave a trail.», «Ralph Waldo Emerson» ),
array( «The mind is everything. What you think you become.», «Buddha» ),
array( «Water which is too pure has no fish.», «Chinese Proverb» ),
array( «A smooth sea never made a skilled sailor.», «African Proverb» ),
array( «He that can have patience can have what he will.», «Benjamin Franklin» ),
array( «You miss 100% of the shots you don’t take.», «Wayne Gretzky» ),
array( «Life is what happens when you’re busy making other plans.», «John Lennon» ),
array( «The only impossible journey is the one you never begin.», «Tony Robbins» ),
array( «Not all those who wander are lost.», «J.R.R. Tolkien» ),
array( «It always seems impossible until it’s done.», «Nelson Mandela» ),
array( «Do what you can, with what you have, where you are.», «Theodore Roosevelt» ),
array( «If you want to go fast, go alone. If you want to go far, go together.», «African Proverb» ),
array( «The best time to plant a tree was 20 years ago. The second best time is now.», «Chinese Proverb» ),
array( «What lies behind us and what lies before us are tiny matters compared to what lies within us.», «Ralph Waldo Emerson» ),
array( «Turn your wounds into wisdom.», «Oprah Winfrey» ),
array( «Knowing yourself is the beginning of all wisdom.», «Aristotle» ),
array( «The unexamined life is not worth living.», «Socrates» ),
array( «He who has a why to live can bear almost any how.», «Friedrich Nietzsche» ),
array( «Out of difficulties grow miracles.», «Jean de La Bruyère» ),
array( «A journey of a thousand miles must begin with a single step.», «Lao Tzu» ),
array( «Even a small star shines in the darkness.», «Finnish Proverb» ),
array( «The bamboo that bends is stronger than the oak that resists.», «Japanese Proverb» ),
array( «Better to light a candle than curse the darkness.», «Chinese Proverb» ),
array( «When the winds of change blow, some build walls, others build windmills.», «Chinese Proverb» ),
array( «Fall down seven times, get up eight.», «Japanese Proverb» ),
array( «One who plants a tree knows they will not sit in its shade.», «Indian Proverb» ),
array( «The nail that sticks out gets hammered down.», «Japanese Proverb» ),
array( «A single arrow is easily broken, but not ten in a bundle.», «Japanese Proverb» ),
array( «He who has never failed has never tried anything new.», «African Proverb» ),
array( «Even the greatest of trees was once a nut who held its ground.», «American Proverb» ),
array( «The river that forgets its source will dry up.», «African Proverb» ),
array( «A bird does not sing because it has an answer, it sings because it has a song.», «Chinese Proverb» ),
array( «Patience is bitter, but its fruit is sweet.», «Aristotle» ),
array( «He who is not courageous enough to take risks will accomplish nothing in life.», «Muhammad Ali» ),
array( «The best preparation for tomorrow is doing your best today.», «H. Jackson Brown Jr.» ),
array( «Success usually comes to those who are too busy to be looking for it.», «Henry David Thoreau» ),
array( «Do not wait for the perfect moment, take the moment and make it perfect.», «Unknown» ),
array( «Great things are done by a series of small things brought together.», «Vincent van Gogh» ),
array( «Perseverance is not a long race; it is many short races one after the other.», «Walter Elliot» ),
array( «You cannot cross the sea merely by standing and staring at the water.», «Rabindranath Tagore» ),
);

foreach ( $frases_iniciales as $f ) {
$wpdb->insert( $tabla, array( ‘texto’ => $f[0], ‘autor’ => $f[1] ) );
}
}
}
register_activation_hook( __FILE__, ‘fa_activar_plugin’ );

/* ==========================================================
* FUNCIONES DE ACCESO A DATOS
* ========================================================== */
function fa_obtener_frases() {
global $wpdb;
$tabla = $wpdb->prefix . FA_TABLA;
return $wpdb->get_results( «SELECT id, texto, autor FROM $tabla ORDER BY id ASC» );
}

function fa_obtener_frases_json() {
$frases = fa_obtener_frases();
$salida = array();
foreach ( $frases as $f ) {
$salida[] = array( ‘texto’ => $f->texto, ‘autor’ => $f->autor );
}
return wp_json_encode( $salida );
}

/* ==========================================================
* RENDERIZADO EN PORTADA (widget y shortcode)
* Incluye enlace de traducción automática al castellano
* (Google Translate), generado por JS según la frase mostrada.
* ========================================================== */
function fa_render_markup_y_script( $instancia_id ) {
ob_start();
?>
<div id=»fa-widget-<?php echo esc_attr( $instancia_id ); ?>» class=»fa-widget» style=»font-family: Georgia, serif; padding: 16px; border-left: 4px solid #444; background: #f9f9f9;»>
<p class=»fa-texto» style=»font-style: italic; font-size: 16px; line-height: 1.5; margin: 0 0 8px 0;»></p>
<p class=»fa-autor» style=»text-align: right; font-weight: bold; margin: 0 0 8px 0;»></p>
<p style=»text-align: right; margin: 0 0 12px 0;»>
<a class=»fa-traducir» href=»#» target=»_blank» rel=»noopener noreferrer» style=»font-size: 13px; text-decoration: underline;»>Traducir al castellano</a>
</p>
<button class=»fa-btn» type=»button» style=»cursor: pointer; padding: 6px 14px; border: none; border-radius: 4px; background: #444; color: #fff; font-size: 14px;»>Otra frase</button>
</div>
<script>
(function() {
var frases = <?php echo fa_obtener_frases_json(); ?>;
var contenedor = document.getElementById(‘fa-widget-<?php echo esc_js( $instancia_id ); ?>’);
if (!contenedor || !frases.length) return;
var elTexto = contenedor.querySelector(‘.fa-texto’);
var elAutor = contenedor.querySelector(‘.fa-autor’);
var elTraducir = contenedor.querySelector(‘.fa-traducir’);
var elBtn = contenedor.querySelector(‘.fa-btn’);

function mostrarFraseAleatoria() {
var indice = Math.floor(Math.random() * frases.length);
var frase = frases[indice];
elTexto.textContent = ‘\u201C’ + frase.texto + ‘\u201D’;
elAutor.textContent = ‘\u2014 ‘ + frase.autor;
elTraducir.href = ‘https://translate.google.com/?sl=en&tl=es&text=’ + encodeURIComponent(frase.texto) + ‘&op=translate’;
}

elBtn.addEventListener(‘click’, mostrarFraseAleatoria);
mostrarFraseAleatoria();
})();
</script>
<?php
return ob_get_clean();
}

function fa_shortcode_frases_aleatorias() {
static $contador = 0;
$contador++;
return fa_render_markup_y_script( ‘sc-‘ . $contador );
}
add_shortcode( ‘frases_aleatorias’, ‘fa_shortcode_frases_aleatorias’ );

class FA_Widget_Frases extends WP_Widget {
public function __construct() {
parent::__construct(
‘fa_widget_frases’,
__( ‘Frase Aleatoria’, ‘frases-aleatorias’ ),
array( ‘description’ => __( ‘Muestra una frase célebre al azar, con enlace de traducción y botón para cambiarla.’, ‘frases-aleatorias’ ) )
);
}

public function widget( $args, $instance ) {
echo $args[‘before_widget’];
if ( ! empty( $instance[‘titulo’] ) ) {
echo $args[‘before_title’] . apply_filters( ‘widget_title’, $instance[‘titulo’] ) . $args[‘after_title’];
}
echo fa_render_markup_y_script( ‘widget-‘ . $this->id );
echo $args[‘after_widget’];
}

public function form( $instance ) {
$titulo = ! empty( $instance[‘titulo’] ) ? $instance[‘titulo’] : __( ‘Frase del día’, ‘frases-aleatorias’ );
?>
<p>
<label for=»<?php echo esc_attr( $this->get_field_id( ‘titulo’ ) ); ?>»><?php _e( ‘Título:’, ‘frases-aleatorias’ ); ?></label>
<input class=»widefat» id=»<?php echo esc_attr( $this->get_field_id( ‘titulo’ ) ); ?>» name=»<?php echo esc_attr( $this->get_field_name( ‘titulo’ ) ); ?>» type=»text» value=»<?php echo esc_attr( $titulo ); ?>»>
</p>
<?php
}

public function update( $new_instance, $old_instance ) {
$instance = array();
$instance[‘titulo’] = ( ! empty( $new_instance[‘titulo’] ) ) ? sanitize_text_field( $new_instance[‘titulo’] ) : »;
return $instance;
}
}
add_action( ‘widgets_init’, function () {
register_widget( ‘FA_Widget_Frases’ );
} );

/* ==========================================================
* PANEL DE ADMINISTRACIÓN: listar, añadir, editar, eliminar
* ========================================================== */
add_action( ‘admin_menu’, function () {
add_menu_page(
‘Frases Aleatorias’,
‘Frases Aleatorias’,
‘manage_options’,
‘fa-frases’,
‘fa_pagina_admin’,
‘dashicons-format-quote’,
58
);
} );

function fa_pagina_admin() {
if ( ! current_user_can( ‘manage_options’ ) ) {
return;
}

global $wpdb;
$tabla = $wpdb->prefix . FA_TABLA;

/* ———- Procesar acciones enviadas por formulario ———- */
if ( isset( $_POST[‘fa_accion’] ) && check_admin_referer( ‘fa_guardar_frase’, ‘fa_nonce’ ) ) {
$texto = isset( $_POST[‘fa_texto’] ) ? sanitize_textarea_field( wp_unslash( $_POST[‘fa_texto’] ) ) : »;
$autor = isset( $_POST[‘fa_autor’] ) ? sanitize_text_field( wp_unslash( $_POST[‘fa_autor’] ) ) : »;

if ( $texto !== » && $autor !== » ) {
if ( $_POST[‘fa_accion’] === ‘crear’ ) {
$wpdb->insert( $tabla, array( ‘texto’ => $texto, ‘autor’ => $autor ) );
echo ‘<div class=»notice notice-success is-dismissible»><p>Frase añadida.</p></div>’;
} elseif ( $_POST[‘fa_accion’] === ‘actualizar’ && ! empty( $_POST[‘fa_id’] ) ) {
$wpdb->update( $tabla, array( ‘texto’ => $texto, ‘autor’ => $autor ), array( ‘id’ => (int) $_POST[‘fa_id’] ) );
echo ‘<div class=»notice notice-success is-dismissible»><p>Frase actualizada.</p></div>’;
}
} else {
echo ‘<div class=»notice notice-error is-dismissible»><p>El texto y el autor no pueden estar vacíos.</p></div>’;
}
}

if ( isset( $_GET[‘fa_eliminar’] ) && check_admin_referer( ‘fa_eliminar_frase’ ) ) {
$wpdb->delete( $tabla, array( ‘id’ => (int) $_GET[‘fa_eliminar’] ) );
echo ‘<div class=»notice notice-success is-dismissible»><p>Frase eliminada.</p></div>’;
}

/* ———- ¿Estamos editando una frase? ———- */
$editando = null;
if ( isset( $_GET[‘fa_editar’] ) ) {
$editando = $wpdb->get_row( $wpdb->prepare( «SELECT * FROM $tabla WHERE id = %d», (int) $_GET[‘fa_editar’] ) );
}

$frases = fa_obtener_frases();
?>
<div class=»wrap»>
<h1>Frases Aleatorias</h1>
<p>Shortcode: <code>

</code> · También disponible como widget en Apariencia → Widgets.</p>

<h2><?php echo $editando ? ‘Editar frase’ : ‘Añadir nueva frase’; ?></h2>
<form method=»post» style=»max-width: 600px;»>
<?php wp_nonce_field( ‘fa_guardar_frase’, ‘fa_nonce’ ); ?>
<input type=»hidden» name=»fa_accion» value=»<?php echo $editando ? ‘actualizar’ : ‘crear’; ?>»>
<?php if ( $editando ) : ?>
<input type=»hidden» name=»fa_id» value=»<?php echo esc_attr( $editando->id ); ?>»>
<?php endif; ?>

<table class=»form-table»>
<tr>
<th><label for=»fa_texto»>Texto (inglés)</label></th>
<td><textarea name=»fa_texto» id=»fa_texto» rows=»3″ class=»large-text» required><?php echo $editando ? esc_textarea( $editando->texto ) : »; ?></textarea></td>
</tr>
<tr>
<th><label for=»fa_autor»>Autor</label></th>
<td><input type=»text» name=»fa_autor» id=»fa_autor» class=»regular-text» required value=»<?php echo $editando ? esc_attr( $editando->autor ) : »; ?>»></td>
</tr>
</table>

<?php submit_button( $editando ? ‘Actualizar frase’ : ‘Añadir frase’ ); ?>
<?php if ( $editando ) : ?>
<a href=»<?php echo esc_url( admin_url( ‘admin.php?page=fa-frases’ ) ); ?>» class=»button»>Cancelar edición</a>
<?php endif; ?>
</form>

<h2>Frases existentes (<?php echo count( $frases ); ?>)</h2>
<table class=»widefat striped»>
<thead>
<tr>
<th style=»width: 55%;»>Texto</th>
<th>Autor</th>
<th style=»width: 160px;»>Acciones</th>
</tr>
</thead>
<tbody>
<?php foreach ( $frases as $f ) : ?>
<tr>
<td><?php echo esc_html( $f->texto ); ?></td>
<td><?php echo esc_html( $f->autor ); ?></td>
<td>
<a href=»<?php echo esc_url( admin_url( ‘admin.php?page=fa-frases&fa_editar=’ . $f->id ) ); ?>»>Editar</a>
|
<a href=»<?php echo esc_url( wp_nonce_url( admin_url( ‘admin.php?page=fa-frases&fa_eliminar=’ . $f->id ), ‘fa_eliminar_frase’ ) ); ?>»
onclick=»return confirm(‘¿Eliminar esta frase?’);» style=»color: #b32d2e;»>Eliminar</a>
|
<a href=»https://translate.google.com/?sl=en&tl=es&text=<?php echo rawurlencode( $f->texto ); ?>&op=translate» target=»_blank» rel=»noopener noreferrer»>Traducir</a>
</td>
</tr>
<?php endforeach; ?>
<?php if ( empty( $frases ) ) : ?>
<tr><td colspan=»3″>Todavía no hay frases.</td></tr>
<?php endif; ?>
</tbody>
</table>
</div>
<?php
}

Script para cargar frases aleatorias como gadget en Blogger

Valencia, 28/08/2026, G.B.
Un script para cargar 50 frases de forma aleatoria en un gadget de Blogger. En breve estará disponible como plugin para WordPress con la misma función.

Aquí dejo el código con las instrucciones para insertarlo como gadget de Blogger. Yo lo he llamado «Some Thoughts…». Incluye 50 frases (en inglés, eso sí). El ejemplo en Blogger puedes verlo aquí y también en WordPress como un widget el pie de página de esta misma web. Repito, habrá un plugin para WordPress que incluya administración de las frases o «pensamientos».

Sin más, este es el código:

<!-- 
GADGET DE FRASES ALEATORIAS PARA BLOGGER
Cómo instalarlo:
1. En el panel de Blogger, ve a Diseño > Añadir un gadget.
2. Elige "HTML/JavaScript".
3. Pega todo este código en el cuadro de contenido.
4. Guarda.
-->

<div id="frase-widget" style="font-family: Georgia, serif; padding: 16px; border-left: 4px solid #444; background: #f9f9f9; max-width: 100%; box-sizing: border-box;">
<p id="frase-texto" style="font-style: italic; font-size: 16px; line-height: 1.5; margin: 0 0 8px 0;"></p>

<p id="frase-autor" style="text-align: right; font-weight: bold; margin: 0 0 12px 0;"></p>

<button id="frase-btn" style="cursor: pointer; padding: 6px 14px; border: none; border-radius: 4px; background: #444; color: #fff; font-size: 14px;">
Otra frase
</button>
</div>

<script>
(function() {
var frases = [
{
texto: "He who blames others has a long way to go on his journey. He who blames himself is halfway there. He who blames no one has arrived.",
autor: "Chinese Proverb"
},
{
texto: "The only way to do great work is to love what you do.",
autor: "Steve Jobs"
},
{
texto: "In the middle of difficulty lies opportunity.",
autor: "Albert Einstein"
},
{
texto: "It does not matter how slowly you go as long as you do not stop.",
autor: "Confucius"
},
{
texto: "Fall seven times, stand up eight.",
autor: "Japanese Proverb"
},
{
texto: "The journey of a thousand miles begins with a single step.",
autor: "Lao Tzu"
},
{
texto: "Whether you think you can or you think you can't, you're right.",
autor: "Henry Ford"
},
{
texto: "The best way to predict the future is to create it.",
autor: "Abraham Lincoln"
},
{
texto: "Success is not final, failure is not fatal: it is the courage to continue that counts.",
autor: "Winston Churchill"
},
{
texto: "Believe you can and you're halfway there.",
autor: "Theodore Roosevelt"
},
{
texto: "Do not go where the path may lead, go instead where there is no path and leave a trail.",
autor: "Ralph Waldo Emerson"
},
{
texto: "The mind is everything. What you think you become.",
autor: "Buddha"
},
{
texto: "Water which is too pure has no fish.",
autor: "Chinese Proverb"
},
{
texto: "A smooth sea never made a skilled sailor.",
autor: "African Proverb"
},
{
texto: "He that can have patience can have what he will.",
autor: "Benjamin Franklin"
},
{
texto: "You miss 100% of the shots you don't take.",
autor: "Wayne Gretzky"
},
{
texto: "Life is what happens when you're busy making other plans.",
autor: "John Lennon"
},
{
texto: "The only impossible journey is the one you never begin.",
autor: "Tony Robbins"
},
{
texto: "Not all those who wander are lost.",
autor: "J.R.R. Tolkien"
},
{
texto: "It always seems impossible until it's done.",
autor: "Nelson Mandela"
},
{
texto: "Do what you can, with what you have, where you are.",
autor: "Theodore Roosevelt"
},
{
texto: "If you want to go fast, go alone. If you want to go far, go together.",
autor: "African Proverb"
},
{
texto: "The best time to plant a tree was 20 years ago. The second best time is now.",
autor: "Chinese Proverb"
},
{
texto: "What lies behind us and what lies before us are tiny matters compared to what lies within us.",
autor: "Ralph Waldo Emerson"
},
{
texto: "Turn your wounds into wisdom.",
autor: "Oprah Winfrey"
},
{
texto: "Knowing yourself is the beginning of all wisdom.",
autor: "Aristotle"
},
{
texto: "The unexamined life is not worth living.",
autor: "Socrates"
},
{
texto: "He who has a why to live can bear almost any how.",
autor: "Friedrich Nietzsche"
},
{
texto: "Out of difficulties grow miracles.",
autor: "Jean de La Bruyère"
},
{
texto: "A journey of a thousand miles must begin with a single step.",
autor: "Lao Tzu"
},
{
texto: "Even a small star shines in the darkness.",
autor: "Finnish Proverb"
},
{
texto: "The bamboo that bends is stronger than the oak that resists.",
autor: "Japanese Proverb"
},
{
texto: "Better to light a candle than curse the darkness.",
autor: "Chinese Proverb"
},
{
texto: "When the winds of change blow, some build walls, others build windmills.",
autor: "Chinese Proverb"
},
{
texto: "Fall down seven times, get up eight.",
autor: "Japanese Proverb"
},
{
texto: "One who plants a tree knows they will not sit in its shade.",
autor: "Indian Proverb"
},
{
texto: "The nail that sticks out gets hammered down.",
autor: "Japanese Proverb"
},
{
texto: "A single arrow is easily broken, but not ten in a bundle.",
autor: "Japanese Proverb"
},
{
texto: "He who has never failed has never tried anything new.",
autor: "African Proverb"
},
{
texto: "Even the greatest of trees was once a nut who held its ground.",
autor: "American Proverb"
},
{
texto: "The river that forgets its source will dry up.",
autor: "African Proverb"
},
{
texto: "A bird does not sing because it has an answer, it sings because it has a song.",
autor: "Chinese Proverb"
},
{
texto: "Patience is bitter, but its fruit is sweet.",
autor: "Aristotle"
},
{
texto: "He who is not courageous enough to take risks will accomplish nothing in life.",
autor: "Muhammad Ali"
},
{
texto: "The best preparation for tomorrow is doing your best today.",
autor: "H. Jackson Brown Jr."
},
{
texto: "Success usually comes to those who are too busy to be looking for it.",
autor: "Henry David Thoreau"
},
{
texto: "Do not wait for the perfect moment, take the moment and make it perfect.",
autor: "Unknown"
},
{
texto: "Great things are done by a series of small things brought together.",
autor: "Vincent van Gogh"
},
{
texto: "Perseverance is not a long race; it is many short races one after the other.",
autor: "Walter Elliot"
},
{
texto: "You cannot cross the sea merely by standing and staring at the water.",
autor: "Rabindranath Tagore"
}
];

function mostrarFraseAleatoria() {
var indice = Math.floor(Math.random() * frases.length);
var frase = frases[indice];

// Comillas dobles normales
document.getElementById('frase-texto').textContent =
'"' + frase.texto + '"';

// Guion normal en lugar del guion largo
document.getElementById('frase-autor').textContent =
'- ' + frase.autor;
}

document
.getElementById('frase-btn')
.addEventListener('click', mostrarFraseAleatoria);

mostrarFraseAleatoria();
})();
</script>

Métodos de instalación de programas en Linux: apt, snap, Flatpak y AppImage

flatpak

Valencia, 26/08/2026, G.B.
Si vienes de Windows o macOS, seguro que lo tienes claro: descargas un instalador, haces doble clic y listo. En Linux la cosa es distinta, porque no hay un solo sistema de instalación, sino varios que conviven. Esto puede resultar confuso al principio, pero cada uno tiene su razón de ser. Vamos a repasarlos de forma sencilla.

APT: el clásico de Debian y Ubuntu

apt (Advanced Package Tool) es el gestor de paquetes tradicional de las distribuciones basadas en Debian, como Ubuntu, Linux Mint o Pop!_OS. Funciona conectándose a unos repositorios oficiales (servidores donde están alojados los programas ya compilados y listos para tu sistema) y descargando desde ahí. Personalmente es el método que más utilizo, y sospecho que la mayoría de los usuarios de GNU LInux.

Ejemplo de uso básico:

sudo apt update        # actualiza la lista de paquetes disponibles
sudo apt install firefox   # instala un programa
sudo apt remove firefox    # lo desinstala

Ventajas:
– Muy integrado con el sistema, gestiona bien las dependencias (las «piezas» que un programa necesita para funcionar).
– Ligero: no duplica librerías entre programas.
– Es el método más estable y probado.

Inconvenientes:
– Los paquetes de los repositorios oficiales a veces no son la última versión del programa, ya que priorizan estabilidad.
– Solo funciona en distribuciones basadas en Debian/Ubuntu (otras familias usan dnf, pacman, etc.).

Snap: el sistema universal de Canonical

Snap es una tecnología creada por Canonical (la empresa detrás de Ubuntu) para poder distribuir programas que funcionen igual en cualquier distribución Linux, sin depender de sus repositorios específicos.

La idea clave es que un «snap» incluye, dentro del propio paquete, casi todas las dependencias que necesita para funcionar. Por eso ocupan más espacio, pero a cambio son más independientes del sistema.

sudo snap install spotify
sudo snap remove spotify

Ventajas:
– Últimas versiones de los programas, normalmente actualizadas de forma automática.
– Funciona en teoría en cualquier distribución.
– Aislamiento (sandboxing): cada snap corre en un entorno algo separado del sistema, lo que aporta seguridad.

Inconvenientes:
– Suelen tardar algo más en abrirse (arranque más lento).
– Ocupan más espacio en disco.
– Dependen de una tienda centralizada (Snap Store) controlada por Canonical, lo que a algunos usuarios no les convence.

Flatpak / Flathub: la alternativa independiente

Flatpak es un sistema similar a Snap en su filosofía (paquetes autocontenidos que funcionan en cualquier distribución), pero desarrollado de forma independiente, sin depender de una única empresa. Flathub es la tienda o repositorio más popular donde se publican aplicaciones en formato Flatpak.

flatpak install flathub org.videolan.VLC
flatpak run org.videolan.VLC

Ventajas:
– No depende de ninguna empresa concreta; el proyecto es más comunitario.
– También aísla las aplicaciones del sistema, mejorando la seguridad.
– Muy usado por muchas distribuciones (Fedora lo trae integrado de serie, por ejemplo).

Inconvenientes:
– Al igual que Snap, ocupa más espacio en disco por duplicar dependencias.
– Requiere instalar primero el propio Flatpak y añadir el repositorio de Flathub si tu distribución no lo trae ya configurado.

AppImage: el programa «portable»

AppImage es un enfoque diferente: no es un gestor de paquetes con instalación y desinstalación como tal, sino un único archivo ejecutable que contiene toda la aplicación y lo necesario para funcionar. Se parece un poco a los ejecutables portables de Windows.

Uso típico:

  1. Descargas el archivo .AppImage desde la web del programa.
  2. Le das permisos de ejecución:
    bash
    chmod +x programa.AppImage
  3. Lo ejecutas directamente haciendo doble clic o desde terminal:
    bash
    ./programa.AppImage

Ventajas:
– No requiere instalación ni permisos de administrador.
– Puedes tener varias versiones del mismo programa en distintas carpetas sin conflictos.
– Es muy fácil de eliminar: basta con borrar el archivo.

Inconvenientes:
– No se actualiza solo (salvo que el propio programa incluya un sistema de actualización interno).
– No aparece integrado en el menú de aplicaciones por defecto (aunque hay herramientas como AppImageLauncher que solucionan esto).
– Cada AppImage lleva sus propias dependencias, así que también ocupa más espacio.

¿Cuál elegir?

No hay una respuesta única, depende de lo que busques:

  • apt: para el uso diario y programas del sistema, priorizando estabilidad e integración.
  • Snap: si tu distribución lo trae de serie (como Ubuntu) y quieres comodidad con actualizaciones automáticas.
  • Flatpak/Flathub: buena alternativa a Snap, especialmente en distribuciones que no son de la familia Ubuntu.
  • AppImage: ideal para probar un programa puntual, sin instalarlo «de verdad», o cuando quieres portabilidad total.

En la práctica, es habitual combinar varios métodos en un mismo sistema: apt para la base y herramientas del sistema, y Snap, Flatpak o AppImage para aplicaciones concretas que necesitas siempre en su última versión. No hay nada de malo en ello, cada herramienta cubre una necesidad distinta.

Feliz 35 cumpleaños, Linux

«Estoy haciendo un sistema operativo (gratuito) (solo un pasatiempo, no será grande y profesional como gnu)…» El 25 de agosto de 1991, un estudiante de 21 años envió un mensaje a un grupo de noticias de Usenet. Pensaba que solo estaba construyendo algo divertido para sí mismo. 35 años después, ese «pasatiempo» es la columna vertebral de la tecnología global:

Nicotine, la alternativa a SoulSeek

nicotine

Valencia, 25/08/2026, G.B.
Hoy me he encontrado en este mismo sitio un viejo post sobre el programa P2P (Peer To Peer) SoulSeek. Aunque lo tenía instalado en Ubuntu Mate 22.0.4, he intentado instalarlo en el portátil con el cual estoy redactando esta entrada y no ha habido forma de arrancar el .AppImage. Así pues, en vez de enredarme con las posibles causas, he decidido instalar una alternativa: Nicotine+.

nicotine interfaz

¿Y de qué va Nicotine+, qué ventajas tiene?

Pues básicamente es un cliente alternativo para la red Soulseek, escrito en Python y con interfaz GTK. Para quien no lo conozca, Soulseek es una red P2P que lleva viva desde el año 2000 (sí, ha llovido), centrada casi en exclusiva en música, y con una comunidad que sigue siendo sorprendentemente activa incluso hoy en día. A diferencia de otras redes P2P más «todoterreno», Soulseek tiene un rollo muy de nicho: colecciones curadas, ripeos de calidad, vinilos digitalizados, rarezas, directos, discografías completas… el paraíso para quien busca algo que no va a encontrar en Spotify.

El cliente oficial de Soulseek existe, pero es cerrado, algo tosco y su soporte en Linux ha sido siempre irregular (de ahí mi intento fallido con el AppImage). Nicotine+ nació precisamente para cubrir ese hueco: es software libre, lleva desarrollándose desde hace más de 20 años, y hoy por hoy es probablemente el cliente más recomendado para usar Soulseek en Linux, aunque también hay versiones para Windows y macOS.

Sus ventajas principales, resumidas:

  • Es libre y multiplataforma, con desarrollo activo y actualizaciones frecuentes.
  • Interfaz mucho más cuidada que el cliente oficial, con pestañas para búsquedas, descargas, salas de chat, listas de «buddies», etc.
  • Gestión de descargas y colas bastante más robusta: reintentos automáticos, priorización, control fino de qué compartes y qué no.
  • Ligero: no depende de Electron ni de frameworks pesados, así que consume poquísimos recursos comparado con según qué alternativas.
  • Empaquetado nativo en la mayoría de distros, sin depender de un AppImage suelto que un día decide no arrancar (como me ha pasado a mí).

Cómo instalar Nicotine+ en Linux (Ubuntu MATE / derivados de Ubuntu-Debian)

Aquí es donde la cosa se pone fácil, porque Nicotine+ está en los repositorios oficiales de Ubuntu y Debian desde hace tiempo. Lo más directo, vía terminal (lo que yo he hecho):

sudo apt update
sudo apt install nicotine

Sí, el paquete se llama simplemente nicotine, aunque el proyecto se llame Nicotine+. Cosas de la vida del software libre.

Si tu versión de Ubuntu/Mate trae una versión antigua en repos y quieres ir a la última release, tienes un par de alternativas mejores que tirar de AppImage:

Flatpak (recomendado si quieres la versión más reciente sin líos de dependencias):

flatpak install flathub org.nicotine_plus.Nicotine

PPA oficial del proyecto, si prefieres mantenerte con apt pero con versiones más al día:

sudo add-apt-repository ppa:nicotine-team/stable
sudo apt update
sudo apt install nicotine

Una vez instalado, lo lanzas desde el menú de aplicaciones o con nicotine en terminal, te creas una cuenta en la red Soulseek (es instantáneo, no hace falta verificación de email ni nada raro) y ya puedes empezar a buscar. Eso sí: como en toda red P2P, comparte algo de tu propia colección — la etiqueta no escrita de Soulseek es que si solo descargas y no compartes nada, tarde o temprano notarás que las descargas te van más lentas o directamente algunos usuarios te ignoran.

En el próximo post os cuento cómo tengo configurada la carpeta compartida y algunos trucos de búsqueda para encontrar justo lo que buscáis sin perderos entre resultados basura.

Para reflexionar…

Valencia, 23/08/2026
Aunque esto ya lo he visto varias veces, considero que vale la pena «recordárnoslo». Y creo sinceramente que vamos a más, a mucho más…

Aquí va:

NOTA sobre el código embebido de x.com (ex-twitter):

Mini guía de comandos esenciales de Linux

mini guia comandos linux

Valencia, 23/08/2026, G.B.
¿Cuántos comandos de Linux crees que necesitas saber de memoria para manejarte bien en una terminal? La respuesta probablemente sea muchos menos de los que imaginas.

Cuando empiezas con Linux es fácil pensar que necesitas memorizar una lista interminable de comandos, opciones y parámetros. Pero la terminal no funciona como un examen de memoria: la clave está en saber qué quieres hacer y conocer las herramientas que pueden ayudarte a conseguirlo.

En esta (mini) guía he reunido algunos de los comandos que más vas a utilizar, organizados por categorías.

Antes de entrar en materia conviene tener clara una idea: cada comando puede tener atributos, también llamados opciones o flags, que modifican su comportamiento. Suelen escribirse precedidos de un guion (-l) o de doble guion (--long), y pueden combinarse entre sí, como en ls -la. No hace falta memorizarlos: basta con consultar la ayuda del propio comando siempre que la necesites, con man nombre_del_comando para ver el manual completo o con nombre_del_comando --help para un resumen rápido de sus opciones. Por ejemplo, man ls o ls --help te mostrarán todos los atributos disponibles para listar archivos.

Aquí la tenéis:

📍 Orientarte

ComandoUsoEjemplo
pwdSaber dónde estáspwd
lsListar archivos y directoriosls -la
cdMoverte por el sistemacd /var/www/html
treeVer la estructura de carpetas en árboltree -L 2

📁 Trabajar con archivos

ComandoUsoEjemplo
touchCrear archivostouch index.html
mkdirCrear directoriosmkdir -p proyecto/css
cpCopiarcp -r sitio/ sitio_backup/
mvMover o renombrarmv borrador.txt final.txt
rmEliminarrm -rf carpeta_temporal/
chmodCambiar permisoschmod 644 config.php
chownCambiar propietariochown www-data:www-data index.php

👀 Consultar información

ComandoUsoEjemplo
catMostrar el contenido completocat wp-config.php
lessVisor paginado y navegableless error.log
headPrimeras líneas de un archivohead -n 20 access.log
tailÚltimas líneas, útil en tiempo realtail -f error.log
wcContar líneas, palabras o caractereswc -l access.log

🔎 Buscar

ComandoUsoEjemplo
findLocalizar archivos por criteriosfind /home -name "*.txt"
grepBuscar texto dentro de archivosgrep -r "TODO" ./src
locateBúsqueda rápida usando índice del sistemalocate httpd.conf

⚙️ Sistema y procesos

ComandoUsoEjemplo
dfEspacio en disco por particióndf -h
duEspacio usado por carpetadu -sh /var/www/*
freeMemoria RAM disponiblefree -h
unameInformación del sistemauname -a
psProcesos en ejecuciónps aux
topMonitor de procesos en tiempo realtop
killTerminar un proceso por PIDkill -9 1234
systemctlGestionar servicios del sistemasystemctl status apache2

🌐 Red

ComandoUsoEjemplo
ipConfiguración de redip a
pingComprobar conectividadping -c 4 8.8.8.8
ssConexiones y puertos activosss -tulpn
curlHacer peticiones a URLs, probar APIscurl -I https://indaga.net

🆘 Ayuda

ComandoUsoEjemplo
manManual completo de un comandoman grep
--helpResumen rápido de opcionesrsync --help
whatisDescripción de una línea de un comandowhatis tail

Combinaciones útiles

Encadenar comandos con el símbolo | (pipe) multiplica su potencia, por ejemplo:

ps aux | grep firefox

permite localizar procesos relacionados con Firefox, mientras que:

find /home -name "*.txt"

busca archivos .txt dentro de /home.

Conclusión

Lo más importante es que no necesitas aprenderlo todo de golpe. Aprende primero para qué sirve cada herramienta y úsala cuando realmente la necesites. Con la práctica, los comandos que más utilizas acabarán saliendo solos.

Por qué desactivar Disable Comments no basta para reabrir los comentarios en WordPress

comentarios wordpress

Valencia, 22/08/2026, G.B.
Hace unos días me encontré con un problema aparentemente sencillo que terminó siendo bastante más terco de lo esperado: quería volver a permitir comentarios en las entradas de este blog, y por más que lo intentaba, el formulario para escribir un comentario nuevo seguía sin aparecer. Los comentarios antiguos se veían perfectamente, pero no había forma de dejar uno nuevo.

El sospechoso obvio: Disable Comments

En algún momento había usado el plugin Disable Comments para bloquear los comentarios en ciertos tipos de contenido. Cuando decidí revertir eso, hice lo que parecía razonable:

  1. Entré en los ajustes del plugin y cambié el tipo de contenido (la taxonomía de entradas) para el que se bloqueaban los comentarios, dejando «Entradas» fuera del bloqueo.
  2. Guardé los cambios.
  3. Desactivé el plugin.

Nada. Los comentarios seguían sin poder crearse.

Pensando que quizás el plugin no se había desactivado del todo bien, fui un paso más allá:

  1. Lo desinstalé por completo, borrándolo del sitio.
  2. Vacié toda la caché, tanto la del plugin de caché del sitio como cualquier caché a nivel de servidor.

Seguía sin funcionar. En este punto ya no tenía sentido seguir sospechando del plugin: ni siquiera estaba instalado.

Reduciendo el problema

Revisé el archivo single.php del tema por si el formulario de comentarios se estaba omitiendo por algún motivo relacionado con la plantilla, pero el código era el estándar, sin nada raro. También descarté que hubiera algún hook personalizado interfiriendo en functions.php del tema hijo: nada relacionado con comments_open, save_post ni wp_insert_post_data.

Lo que sí encontré, consultando directamente la base de datos, fue esto: entradas ya publicadas, incluso una programada, tenían el campo comment_status en closed en la tabla wp_posts, a pesar de que el ajuste general de WordPress (Ajustes > Comentarios > «Permitir que la gente envíe comentarios en las entradas nuevas») estaba correctamente en abierto.

La explicación tiene sentido una vez se entiende cómo funciona WordPress internamente: el ajuste default_comment_status solo se aplica en el momento en que se crea la fila del post por primera vez. Si esa entrada se creó (aunque fuera como borrador) mientras el ajuste global estaba en cerrado, o mientras Disable Comments seguía activo bloqueando ese tipo de contenido, el valor closed queda grabado de forma permanente en esa fila concreta. Cambiar el ajuste general después no reescribe retroactivamente las entradas que ya existían; solo afecta a las que se creen a partir de ese momento.

Es decir, el problema nunca estuvo en el plugin en sí, sino en un dato ya escrito en la base de datos que el plugin dejó como herencia antes de desactivarse.

La solución: una consulta SQL

Con muchas entradas afectadas, corregirlas una a una desde el panel de WordPress (edición en lote, cambiando «Comentarios» a «Permitir») era viable pero lento. La solución rápida fue una actualización directa en la base de datos desde phpMyAdmin:

USE nombre_de_tu_base_de_datos;
UPDATE wp_posts SET comment_status = 'open' WHERE post_type = 'post' AND comment_status = 'closed';

Esta consulta actualiza de golpe el estado de comentarios de todas las entradas (post_type = 'post') que estuvieran cerradas, dejando fuera páginas y otros tipos de contenido. Antes de ejecutarla conviene hacer una copia de seguridad de la tabla wp_posts (tienes que poner tu prefijo de las tablas. Por defecto, si no lo has cambiado en la instalación de WordPress es wp_), y si se quiere comprobar el alcance antes de aplicar el cambio, un simple SELECT COUNT(*) FROM wp_posts WHERE post_type = 'post' AND comment_status = 'closed'; da el número exacto de entradas que se van a ver afectadas.

Conclusión

Cuando un plugin como Disable Comments actúa escribiendo valores directamente en la base de datos en lugar de aplicar solo un filtro en tiempo real, desactivarlo (o incluso desinstalarlo) no revierte esos cambios. Los ajustes generales de WordPress tampoco se aplican de forma retroactiva a entradas ya existentes. En estos casos, revisar directamente la base de datos con una consulta SQL es la forma más fiable de confirmar la causa real del problema y, de paso, la manera más rápida de solucionarlo cuando hay muchas entradas implicadas.

Pexels, fotos y vídeos gratuitos de stock

pexels

Pexels es un banco de imágenes y vídeos gratuitos que se pueden usar sin necesidad de citar la fuente, con miles de creadores compartiendo material de alta calidad libre de regalías. Además del buscador clásico, la web incluye secciones para descubrir tendencias por país, un apartado de retos fotográficos («Challenges»), vídeos gratuitos y su propio blog. Incorpora también un pequeño editor integrado (ajustar colores, añadir texto, recortar, retocar) directamente sobre las fotos antes de descargarlas, y ofrece API para desarrolladores que quieran integrar su catálogo en proyectos propios.

🔗 pexels.com/es-es