Windows 11 y la barra de tareas en la parte superior de la pantalla

Windows 11 ha cambiado muchos aspectos esteticos del sistema operativo de Microsoft. Desde cosas mas o menos tribiales, como por ejemplo el listado de wifis hasta rediseñar completamente el concepto de «Mi PC» (ahora Equipo) o el «Panel de Control».

En mi caso, lo cierto es que la estética no me preocupa demasiado y me adapto bastante rapido a este tipo de cambios, pero con W11 también ha llegado un cambio, probablemente imperceptible para la mayoria de usuarios que, personalmente, me ha provocado un verdadero dolor de cabeza.

Una de las configuraciones (que probablemente no usa prácticamente nadie) que ha desaparecido es el posicionamiento de la barra de tareas.

Desde Windows 98 la posición puedes escogerse, ya sea arrastrando la propia barra hasta el lateral de la pantalla o a la parte superior, pero esta configuración no existe en Windows 11, lo que obliga a tenerla siempre en la parte inferior de la pantalla.

Hasta la fecha, no he averiguado el por qué de eliminar esta configuración, pero después de hartarme de que no añadan esta funcionalidad he estado investigando como volver a poner la barra en la parte superior.

Hasta hace unos meses una manera de ponerla en la parte superior era mediante el registro de windows, modificando la siguiente clave (no entraré en explicar los diferentes valores por que este metodo ya no funciona):

HKEY_CURRENT_USER > Software > Microsoft > Windows > CurrentVersion > Explorer > StuckRects3

Esta opción movia la barra de sitio, pero las miniaturas de las pantallas abiertas que aparecen al poner el raton encima, dejaban de mostrarse. Para mi, esto no es una solución.

Asi que finalmente he dado con un programa que si permite mover la barra a la posicion que quiero y que muestra las miniaturas correctamente.

Explorer Patcher

La solución es simple, instalar el programa Explore Patcher desde la siguiente URL

https://github.com/valinet/ExplorerPatcher/wiki

Este programita permite un montón de personalizaciones, cambiar la ubicación de la barra, recuperar la estética de W10 y muchisimas cosas más.

Para facilitarte el trabajo, he preparado un fichero de configuración para dejar la barra de Windows 11 totalmente operativa con estética de Windows 11 en la parte superior de la pantalla.

Descargar “config de explroer patcher”

ExplorerPatcher_22621.2428.59.1.zip – Descargado 690 veces – 1,48 KB

Para importar la configuración una vez instalado, solo hay que darle botón derecho a la barra de tareas, darle a propiedades y en el menu seleccionar:

About > Import Settings

En este punto seleccionas el fichero de config .reg y listo, ya tienes la barra en la parte superior de tu pantalla!

*Ojo: es probable que necesites reinicar el explorador para que cargue la nueva configuración

Auto-guardado local, no vuelvas a perder nada

Imagínate esta situación: estás trabajando en un informe importante en un editor en línea y, de repente, ¡la conexión a Internet se cae! ¿El resultado? Podrías perder todo tu trabajo. Para evitar este dolor de cabeza digital, el guardado local se convierte en tu mejor amigo. Así que, en un tono amigable y serio, exploremos esta característica y cómo funciona en un editor de informes en Angular.

El Guardado Local: Tu Compañero de Confianza

En este mundo de interrupciones y problemas técnicos constantes, el guardado local es como un héroe digital. Es una función que te permite guardar una copia de tu trabajo en tu dispositivo, asegurando que no se pierda en las nubes de Internet. El código que te voy a mostrar te enseña cómo implementar el guardado local en una aplicación Angular, especialmente en un editor de informes. ¿Las ventajas? Son muchas y muy valiosas.

1. Protección contra la Pérdida de Datos

Nada es más desalentador que perder horas de trabajo debido a problemas de conexión. Con el guardado local, esta pesadilla se desvanece. El código que vamos a ver se encarga de guardar automáticamente el informe en tu dispositivo, garantizando que tus datos estén a salvo, incluso cuando la tormenta digital hace de las suyas.

2. Flexibilidad y Continuidad

El guardado local también te da la libertad que necesitas. ¿Necesitas seguir trabajando en tu informe en un vuelo o en un lugar con una conexión limitada? No hay problema. Con el guardado local, puedes seguir trabajando sin problemas, sin importar dónde estés. Esto te asegura que tu trabajo siga avanzando sin contratiempos.

3. Protección contra Errores Humanos

Quién no ha borrado accidentalmente una sección importante de un informe o ha sobrescrito información crucial. Con el guardado local, los errores humanos son menos aterradores. El sistema te permite restaurar versiones anteriores de tu trabajo, lo que actúa como un salvavidas cuando cometes un error. Así, se reducen las situaciones de estrés y desesperación.

4. Ahorro de Tiempo y Estrés

La amenaza constante de perder datos puede ser estresante y llevar a perder tiempo valioso. El guardado local, en cambio, te ofrece tranquilidad al asegurarte de que tus datos estén respaldados en tu propio dispositivo. Esto ahorra tiempo y esfuerzo, algo realmente valioso en entornos profesionales y personales.

El Código en Acción: Implementación en Angular

Ahora que hemos hablado de los beneficios del guardado local, veamos cómo funciona en una aplicación Angular. En el componente «AutosaveComponent,» el código se encarga de guardar automáticamente el informe en tu dispositivo y de recuperar versiones anteriores en caso de problemas. A continuación, te muestro el código completo y una breve explicación de cómo funciona:

import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import * as moment from 'moment';
import { AuthUserService } from 'projects/cris9/src/app/services/authUser.service';

@Component({
  selector: "app-autosave",
  templateUrl: "./autosave.component.html",
  styleUrls: ["./autosave.component.css"],
})
export class AutosaveComponent implements OnInit {
  @Input()
  set report(value: string) {
    this._report = {report: value, TTL: moment().format("DD/MM/yyyy HH:mm:ss") };
  }

@Input() set report(value: string): Aquí definimos un «setter» para la entrada «report» que nos permite capturar el valor del informe y su marca de tiempo actual. Esto nos ayudará a mantener un registro de los cambios.

@Input() set AN(value: string) { this._AN = value; }

@Input() set AN(value: string): Similar al anterior, este «setter» captura el valor de «AN,» que es parte del contexto del informe.

@Output() restoreTemplate = new EventEmitter<string>();

@Output() restoreTemplate: Esta línea define un evento de salida que nos permitirá restaurar una versión anterior del informe si es necesario.

constructor(private _authUser: AuthUserService) {}

constructor(private _authUser: AuthUserService): Aquí estamos inyectando el servicio «_authUser» para poder utilizarlo en nuestras funciones.

  lastTime;
  lastResult: boolean;
  saving: boolean;
  notSavedYet;
  private _report;
  private _AN;
  private _refreshInterval;

Variables: Estamos declarando varias variables que se usarán en todo el componente para realizar un seguimiento del estado del guardado y la información del informe.

  ngOnInit(): void {
    // Verificar si existe un informe anterior
    if (this.checkExist()) {
      // Iniciar temporizador
      this._refreshInterval = setInterval(() => {
        this.autosave();
      }, 15000);
    }
  }

ngOnInit(): Esta es una función del ciclo de vida de Angular. Aquí comenzamos por verificar si existe un informe anterior (la función checkExist lo hará por nosotros). Si hay un informe anterior, iniciamos un temporizador para el guardado automático con un intervalo de 15 segundos (15000 milisegundos).

  checkExist(): Boolean {
    try {
      if (
        localStorage.getItem(this._authUser.getUser() + "-" + this._AN) !=
          null &&
        localStorage.getItem(
          this._authUser.getUser() + "-" + this._AN + "-" + "lastDate"
        ) != null
      ) {

checkExist(): Esta función verifica si existen datos previamente guardados. Revisamos si hay una entrada en el almacenamiento local para el usuario y para «AN» (parte del contexto del informe), así como la marca de tiempo de la última modificación.

        if (
          (JSON.stringify(JSON.parse(localStorage.getItem(this._authUser.getUser() + "-" + this._AN)).report) != JSON.stringify(this._report.report)) &&
          confirm(
            "Se ha encontrado una versión local del informe con fecha " +
              localStorage.getItem(
                this._authUser.getUser() + "-" + this._AN + "-" + "lastDate"
              ) + " que no ha sido guardada en el servidor." +
              "\n¿Quieres cargarla?\n\n" +
              "** Si no cargas el informe recuperado, se descartará y se sobrescribirá **"
          )
        ) {

Continuación de checkExist(): Aquí, comparamos el informe actual con el informe guardado localmente para ver si ha habido cambios. Si se detectan cambios, mostramos un mensaje de confirmación para cargar la versión local. Si el usuario confirma, recuperamos el informe local y actualizamos el estado.

          this._report = JSON.parse(
            localStorage.getItem(this._authUser.getUser() + "-" + this._AN)
          );
          this.lastTime = localStorage.getItem(
            this._authUser.getUser() + "-" + this._AN + "-" + "lastDate"
          );
          this.lastResult = true;
          this.notSavedYet = false;
          this.restoreTemplate.emit(JSON.stringify(this._report.report));
        }
      } else {
        localStorage.removeItem(this._authUser.getUser() + "-" + this._AN);
        localStorage.removeItem(
          this._authUser.getUser() + "-" + this._AN + "-" + "lastDate"
        );
        this.notSavedYet = true;
      }

Continuación de checkExist(): Si el usuario decide cargar la versión local, aquí actualizamos el estado del informe y marcamos que no se ha guardado aún en el servidor. Si no hay datos locales, eliminamos cualquier información antigua y marcamos que aún no se ha guardado.

      return true;
    } catch (exception) {
      console.log(exception);
      alert("Lo sentimos, se ha producido un error al recuperar el informe :(");
      localStorage.removeItem(this._authUser.getUser() + "-" + this._AN);
      localStorage.removeItem(
        this._authUser.getUser() + "-" + this._AN + "-" + "lastDate"
      );
      this.notSavedYet = true;
      return true;
    }
  }

Continuación de checkExist(): En caso de que ocurra algún error durante la verificación, manejamos la excepción y mostramos un mensaje de alerta. Luego, limpiamos los datos locales y marcamos que no se ha guardado.

autosave() {
    try {
      this.saving = true;
      localStorage.setItem(
        this._authUser.getUser() + "-" + this._AN,
           JSON.stringify(this._report));
      localStorage.setItem(
        this._authUser.getUser() + "-" + this._AN + "-" + "lastDate",
        moment().format("DD/MM/yyyy HH:mm:ss")
      );
      this.lastTime = moment().format("DD/MM/yyyy HH:mm:ss");
      this.lastResult = true;
      this.saving = false;
      this.notSavedYet = false;
    } catch (exception) {
      this.lastTime = moment().format("DD/MM/yyyy HH:mm:ss");
      this.lastResult = false;
      this sving = false;
      this.notSavedYet = false;
    }
  }
}

autosave(): Esta función se encarga de guardar automáticamente el informe en el dispositivo del usuario. Primero, marcamos que estamos en proceso de guardado y luego almacenamos el informe y la marca de tiempo en el almacenamiento local. Si todo va bien, actualizamos el estado y marcamos que no se ha guardado en el servidor. Si ocurre un error, manejamos la excepción y marcamos que no se ha guardado.

Conclusión

En resumen, el guardado local es una función esencial para garantizar la seguridad y la continuidad en la creación de informes en línea. Protege contra la pérdida de datos, ofrece flexibilidad, previene errores humanos y ahorra tiempo. La implementación de esta característica, como se muestra en el código de Angular, es una excelente decisión para crear un editor en línea más amigable y eficiente.

Nuestra intención es ayudarte a que tu experiencia en la creación de informes sea lo más agradable y sin preocupaciones posible. Con el guardado local, puedes estar seguro de que tu trabajo está en buenas manos, incluso cuando las nubes digitales amenazan con tormenta.

Conectar a Oracle con PHP

Hace un par de semanas estuve haciendo unas pruebas consultando una base de datos de Oracle y se me pasó por la cabeza la brillante idea de usar Symfony (un framework de php) para montar las consultas.

Mi sorpresa llegó cuando vi que la instalación por defecto de php no trae un driver para oracle y que al activarlo, apache no arranca.

Después de googlear un poco, todo el mundo recomienda usar el instant client de oracle (que viene a ser como un cliente ligero y portable de oracle) y todos te dicen: «descomprímelo en «X» directorio y añádelo al PATH, con eso te funcionará». Todo mentiras, y si estas aquí probablemente hayas llegado a la misma conclusión.

En primer lugar te diré que yo he optado por usar un XAMMP en mi entorno de desarrollo, por lo que mi manual se basará en configurar XAMMP + PHP + ORACLE, así que si no quieres usar XAMMP, puedes seguir el manual, pero probablemente tengas que instalar manualmente el plugin de oracle para php/apache (hay bastantes manuales por ahí que lo explican).

Dicho esto, empecemos!

1 - Descarga todos los archivos necesarios

*Fíjate que la versión del instant client es la 12.1 (no descargues la 12.2 ya que no te va a funcionar !!!!

2 - Descomprime el Instant Client 12.1

En primer lugar, crea la siguiente ruta y descomprime dentro las tres descargas del instant client 12.1  «C:\php-sdk\oracle\x86\instantclient_12_1» par que queden así:

3 - Instala XAMMP

Esta paso es muy simple, siguiente siguiente y aceptar!
Una vez lo tengas instalado, crea en la raiz de htdocs un documento «phpinfo.php» que contenga en siguiente código:

 <?php phpinfo(); ?>

Ahora accederemos con el navegador a http://localhost/phpinfo.php y nos cargará una pagina con toda la información de php. Aqui buscaremos el apartado «Configure Command»

Si te fijas, en este apartado aparece la ruta que hemos creado antes (si, no me la he sacado de la manga, la creé porque el comando que arranca apache especifica esta ruta en concreto. Podría ser que en versiones posteriores de XAMMP la ruta cambiara, si es así, modifica el directorio para que se adapte.

4 - Crea las variables de entorno

Para que el sistema sepa donde se encuentra el instantclient, tenemos que decírselo en las variables de entorno.

Para ello modificaremos el PATH añadiéndole al inicio la ruta en la que hemos puesto el instant client

Y crearemos una variable nueva que se llame «ORACLE_HOME» con la ruta.

5 - Editamos php.ini

Buscaremos la línea en la que se encuentra el plugin OCI8 de oracle y le quitaremos el comentario (;) del inicio de la línea

Después de esto, deberemos reiniciar, ya que normalmente las variables del PATH no se refrescan de manera correcta.

6 - Arrancamos apache

Si todo ha ido bien y no nos hemos saltado ningún paso, apache arrancará correctamente y podremos conectar a Oracle sin problemas!

Aquí os dejo un código para verificar que funciona:

 <?php 
$conn = oci_connect('user', 'pass', 'ip:1521/service_name');
$query = "sql_string";
$stid = oci_parse($conn, $query);
oci_execute($stid, OCI_DEFAULT);

echo "<table>";
while ($row = oci_fetch_array($stid, OCI_ASSOC)) {
echo "<tr>";
foreach ($row as $item) {
echo "<td> " . $item . " </td>";
} echo "</tr>"; }
echo "</table>";

oci_free_statement($stid);
oci_close($conn); ?>

Si os encontrarais con un error del tipo «Global variable undefined» o «Constant undefined» o similar, es que vuestro sistema no encuentra el instant client, revisa la configuración o renicia si no lo has hecho!

7 - Caracteres españoles

Después de conseguir acceder a oracle, me encontré con que el cliente me retornaba valores «?» en lugar de los caracteres españoles como la «ñ» o los acentos.

Esto es debido a que el instant client parece estar preparado para funcionar a la inglesa (vaya, como todo en la informática). Así que si necesitas acceder a cadenas con texto en castellano, deberás especificarle el NLS_LANG.

Para ello, vamos a tirar de nuevo de las variables del sistema, y al igual que hemos definido el ORACLE_HOME, crearemos una que se llame «NLS_LANG» y le daremos como valor «SPANISH_SPAIN.WE8ISO8859P1»

Con esto ya tendríamos el problema resuelto, pero en muchos foros recomiendan añadir la siguiente línea en el httpd.conf de apache justo al final:

 SetEnv NLS_LANG "SPANISH_SPAIN.WE8ISO8859P1"

En mi caso no fue necesario, pero no está demás comentarlo por si a ti no te funciona. Después de esto, como hemos modificado variables del sistema, reiniciamos de nuevo y a funcionar!

Debes tener en cuenta que cuando desees presentar cualquier texto, no vendrá como UTF8, por lo que deberás pasarle la función utf8_encode() para que lo printee correctamente por pantalla.

Si tienes una array, y quieres convertirla entera, puedes usar la siguiente función

 

public static function array_utf8_encode($dat)
{
if (is_string($dat))
return utf8_encode($dat);
if (!is_array($dat))
return $dat;
$ret = array();
foreach ($dat as $i => $d)
$ret[$i] = self::array_utf8_encode($d);

return $ret;
}

Bonus track

Si has estado atento en la intro, he comentado que usaba symfony para conectar a oracle. Si simplemente quieres lanzar consultas contra oracle, con esto te funcionaría, pero si llevas idea de usar Doctrine (el ORM de symfony) hay unos cuantos pasos que deberás hacer para que funcione correctamente. En el siguiente post, explicaré como se hace ! 

 

Sistema de Monitorización

Hace algún tiempo me encontré un problema a la hora de monitorizar ciertos servidores que se encontraban en centros remotos, así como servicios web que debían estar disponibles. Existen muchas herramientas como Nagios y similares que permiten este tipo de monitorizaciones, pero en muchos casos no querremos montar un servidor sólo para monitorizar dos o tres IP’s y una web.

Como las pequeñas herramientas que encontré por internet no me convencieron demasiado, decidí programar una con funciones de notificación por e-mail, comprobación de ping y consulta web para detectar errores (en caso de que el servidor este online pero, por ejemplo, el servicio web este dando Error 500)

Las funcionalidades de que dispone el programa son:

  • Comprobación de ping
    • Registro de timeouts
    • Tiempo timeout(en ms) configurable
    • Notificación por mail (en la caída y en la recuperación del host)
    • Dos niveles de error (En rojo sin notificación pero con registro, y notificación por mail)
    • Registro de timeouts en fichero
  • Comprobación web
    • Comprobación de estado
    • Búsqueda de cadenas dentro del HTML retornado
    • Notificación por mail
    • Dos niveles de error (En rojo sin notificación pero con registro, y notificación por mail)
    • Registro de timeouts en fichero
  • Posibilidad de mantener la ventana sobre el resto de ventanas (On top)
  • Transparencia al perder el foco
  • Lectura del registro desde la aplicación
  • Archivos de configuración ini

Las configuraciones son las siguientes:
#ips.ini
DESCRIPCION;192.168.XXX.XXX;mail@from.com;mail@to.com;servidorsmtp.com
BDCENTRAL;192.168.1.1;alertas@gmail.com;alexlatorre@me.com;smtp.gmail.com

#webs.ini
WEBCENTRAL;http://miservidor/login.html;mail@from.com;alexlatorre@me.com;smtp.gmail.com;palabras-a-buscar-separadas-por-guiones

 

El sistema de envío de mails no admite servidores con autenticación, por lo que debes ususarlo con un servidor smtp propio o uno anónimo.
Actualmente no tengo previsto modificar el programa, pero si hubiera una buena sugerencia no tengo problema en ponerme a ello!

 

Descargar “PingPong!”

PingPong.zip – Descargado 904 veces – 50,69 KB