xpath

Partimos del siguiente xml:

<?xml version="1.0" encoding="UTF-8"?>

<bookstore>

<book category="cooking">
  <title lang="en">Everyday Italian</title>
  <author>Giada De Laurentiis</author>
  <year>2005</year>
  <price>30.00</price>
</book>

<book category="children">
  <title lang="en">Harry Potter</title>
  <author>J K. Rowling</author>
  <year>2005</year>
  <price>29.99</price>
</book>

<book category="web">
  <title lang="en">XQuery Kick Start</title>
  <author>James McGovern</author>
  <author>Per Bothner</author>
  <author>Kurt Cagle</author>
  <author>James Linn</author>
  <author>Vaidyanathan Nagarajan</author>
  <year>2003</year>
  <price>49.99</price>
</book>

<book category="web">
  <title lang="en">Learning XML</title>
  <author>Erik T. Ray</author>
  <year>2003</year>
  <price>39.95</price>
</book>

</bookstore>

 

y trabajamos con javascript para utilizar xpath y ver los resultados

<!DOCTYPE html>
<html>
<body>

<p id="demo"></p>
<p id="resultado"></p>

<script>




var text = "<bookstore><book category='cooking'>  <title lang='en'>Everyday Italian</title>  <author>Giada De Laurentiis</author>  <year>2005</year>  <price>30.00</price></book><book category='children'>  <title lang='en'>Harry Potter</title>  <author>J K. Rowling</author>  <year>2005</year>  <price>29.99</price></book><book category='web'>  <title lang='en'>XQuery Kick Start</title>  <author>James McGovern</author>  <author>Per Bothner</author>  <author>Kurt Cagle</author>  <author>James Linn</author>  <author>Vaidyanathan Nagarajan</author>  <year>2003</year>  <price>49.99</price></book><book category='web'>  <title lang='en'>Learning XML</title>  <author>Erik T. Ray</author>  <year>2003</year>  <price>39.95</price></book></bookstore>";

path = "/bookstore/book/author";
var parser = new DOMParser();
xmlDoc = parser.parseFromString(text,"text/xml");
 showResult(xmlDoc,path);
 showResult(xmlDoc,"//title");
 showResult(xmlDoc,"//book/author[1]");
 showResult(xmlDoc,"//book[@category=\"web\"]/title");

function showResult(xml,path) {
    var txt = "";
    
    if (xml.evaluate) {
        var nodes = xml.evaluate(path, xml, null, XPathResult.ANY_TYPE, null);
        var result = nodes.iterateNext();
        while (result) {
            txt += result.childNodes[0].nodeValue + "<br>";
            result = nodes.iterateNext();
        } 
    // Code For Internet Explorer
    } else if (window.ActiveXObject || xhttp.responseType == "msxml-document") {
        xml.setProperty("SelectionLanguage", "XPath");
        nodes = xml.selectNodes(path);
        for (i = 0; i < nodes.length; i++) {
            txt += nodes[i].childNodes[0].nodeValue + "<br>";
        }
    }
    document.getElementById('resultado').innerHTML += "<strong>"+path+"</strong><br/>"+txt+"<br>";
}
</script>

</body>
</html>

Y vemos como resultado:

/bookstore/book/author
Giada De Laurentiis
J K. Rowling
James McGovern
Per Bothner
Kurt Cagle
James Linn
Vaidyanathan Nagarajan
Erik T. Ray

//title
Everyday Italian
Harry Potter
XQuery Kick Start
Learning XML

//book/author[1]
Giada De Laurentiis
J K. Rowling
James McGovern
Erik T. Ray

//book[@category="web"]/title
XQuery Kick Start
Learning XML

Se puede interactuar y probar desde la url: aquí

Añadir contador de caracteres restantes a caja de texto

La idea es teniendo una caja de texto o un elemento input añadir un contador de los caracteres que faltan y que se vaya actualizando a medida que escribimos.


para ello utilizamos JQuery y la siguiente libreria:

(function($) {
'use strict';

$.fn.countChar = function() {

return this.each(function(i, element) {
$(element).keyup(function updateCharCounter() {

var $me = $(this);
var maxLength = parseInt($me.attr('maxlength'), 10);
var charCount = $me.val().length;
var nombrecajalimite=$me.prop('id')+"_limit";
var cajalimite = $('#'+nombrecajalimite);

cajalimite.text('' + maxLength + '/' + (maxLength - charCount));
});

var $me = $(this),
maxLength = parseInt($me.attr('maxlength'), 10),
charCount = $me.val().length;
var nombrecajalimite=$me.prop('id')+"_limit";
var cajalimite = $('#'+nombrecajalimite);
if (cajalimite){
$me.after('<p id="'+nombrecajalimite+'" class="limit"></p>');
}
cajalimite = $('#'+nombrecajalimite);
cajalimite.text('' + maxLength + '/' + (maxLength - charCount));

});
};


$( "<style>.limit { float: right; clear: both; margin-bottom: 10px; font: 400 0.688em/1.38 'Roboto'; text-align: right; color: #777777;}</style>" ).appendTo( "head" )



}(jQuery));

Con esto lo único que tenemos que hacer es añadir una clase a nuestra caja de texto:

<div class=" clearfix">
<label class="_text">Caja de texto con maxlength = 50</label>
<input id="nickname" class="cuentacaracteres _textfield" size="32" maxlength="50" placeholder="counter.js">
</div>

Y añadir al final del documento la linea que lo enlaza:

<script>

$(document).ready(function (){
'use strict';

// works on one field
$('.cuentacaracteres').countChar();
});
</script>

Generar un zip de backup del servidor FTP

basandome en el código
he modificado la clase para añadirle seguridad (*):

Para ello partimos de una estructura de servidor:

Donde colocamos el fichero zip.php en el raiz

Dentro del fichero configuramos la ruta donde se van a guardar los archivos y las exclusiones que queremos hacer, en este caso los directorios dir2 y dir4 y el propio fichero de generar el zip.

Una vez que invocamos el fichero desde navegador


obtenemos un zip como:


<?php
	//funciones
	function show($str){
		echo $str . "<br/>\n";
		flush();
		ob_flush();
	}
	function secured($nombre,$directorio){
		return str_replace($directorio."/backup",$directorio."/secured_backup",$nombre);
	}
	
	//configuracion
	ini_set('max_execution_time', 5000);
	$maxFilePerArchive = 20000;
	$directorio="zipBk";
	$debug=false;
	//directorios y ficheros que no quieres que se añadan al archivo
	$excludes=array("dir2","dir4","zip.php");
	
	
	//parametros recibidos
	if (isset($_REQUEST['pass'])){
		$password=$_REQUEST['pass'];
	}else{
		$password="";
	}
	if (isset($_REQUEST['delete'])){
		$delete=true;
	}else{
		$delete=false;
	}
	if (isset($_REQUEST['ver'])){
		$ver=true;
	}else{
		$ver=false;
	}
	if (isset($_REQUEST['dias'])){
		$dias=$_REQUEST['dias'];
	}else{
		$dias=2;
	}
	if (isset($_REQUEST['help'])){
		show("Puedes pasar los parametros<br>
		<strong>pass</strong> Para poner password al zip<br>
		<strong>ver</strong> Para ver los archivos que son, pero sin crear el zip<br>
		<strong>delete</strong> Para borrar los archivos zip anteriores, se puede pasar un parametro dias ( indicando el numero de dias de margen para borrar )<br>
		");
		die;
	}
	
	// increase script timeout value
	
	if ($directorio!="" && $directorio!="." && !file_exists($directorio)) {
		mkdir($directorio, 0777, true);
		if($debug){show("Se ha creado el directorio $directorio porque no existia");}
	}
	if ($delete){
		
		show("Borramos los archivos anteriores a 2 dias");
		$ficheros=0;
		$borrados=0;
		$ahora=time();
		foreach (glob($directorio."/*.zip") as $file) {
			if (is_file($file)){
				$ficheros++;
				
				if ($ahora - filemtime($file) >= 60*60*24*$dias){ // 1 days
					$borrados++;
					echo $file;
					unlink($file);
				}
				echo "<br>";
			}
		}
		show("Hemos borrado $borrados archivos");die;
	}
	
	$numFiles = 0;
	$iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator("./"));
	foreach ($iterator as $key=>$value){
		$key=substr($key,2,strlen($key)-1);
		if (! preg_match('/'.$directorio.'\/backup_'.'/', $key) && !preg_match('/'.$directorio.'\/secured_backup_'.'/', $key)){
			if (!(substr($key,-2)==".." || substr($key,-1)==".")){
				if ($excludes){
					$puede=true;
					foreach ($excludes as $exclude)
					{
						if (preg_match('/^'.$exclude.'/', $key))
						{
							$puede = false;
						} 
					}
				}else{
					$puede=true;
				}
				if ($puede){
					$numFiles += 1;
					if ($ver){
						show("Se insertara el archivo $key");
					}
				}
			}
		}
	}
	show( "Comprimiremos $numFiles archivos" );
	if (!$ver){
		$date = getdate();
		$enlaces=array();
		$splitNum = 0;
		$archive = $directorio."/backup_" . $date[0];
		$currentArchive = $archive . "_" . $splitNum . ".zip";
		$enlaces[]=$currentArchive;
		$zip = new ZipArchive();
		if ($zip->open($currentArchive, ZIPARCHIVE::CREATE) !== TRUE) {
			die ("No se puede abrir el archivo");
		}
		
		
		$iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator("./"));
		$numFiles = 0;
		$counter = 0;
		
		foreach ($iterator as $key=>$value){
			$counter += 1;
			if ($counter >= $maxFilePerArchive) {
				$zip->close();
				if ($password!=""){
					try{
						system('zip -P '.$password.' '.secured($currentArchive).' '.$currentArchive);
						unlink($currentArchive);
					}catch(Exception $ex){
					
					}
				}
				$currentArchive = $archive . "_" . $splitNum++ . ".zip";
				$enlaces[]=$currentArchive;
				show( "Limite de ficheros: partimos el archivo, Nuevo archivo es $currentArchive" ); 
				
				$zip = new ZipArchive();
				if ($zip->open($currentArchive, ZIPARCHIVE::CREATE) !== TRUE) {
					die ("No se puede crear el archivo");
				}
				
				$counter = 0;
			}
			$key=substr($key,2,strlen($key)-1);
			if (! preg_match('/'.$directorio.'\/backup_'.'/', $key) && !preg_match('/'.$directorio.'\/secured_backup_'.'/', $key)){
				if (substr($key,-2)==".." || substr($key,-1)=="."){
					if ($debug){show( "Excluimos $key" );}
				}else{
					if ($excludes){
						$puede=true;
						foreach ($excludes as $exclude)
						{
							if (preg_match('/^'.$exclude.'/', $key))
							{
								$puede = false;
							} 
						}
					}else{
						$puede=true;
					}
					if ($puede){
						$zip->addFile(realpath($key), $key) or die ("ERROR: No se puede añadir el fichero: $key");
						$numFiles += 1;
						if ($debug){show( "Añadimos $key" );}
					}
				}
				
			} else {
				if($debug){show( "No se comprime el archivo,por ser una copia previa-> $key" );}
			}
		}
		// close and save archive
		$zip->addFromString("LEEME_".time().".txt", "#1 Fichero generado automaticamenta\nPor un proceso para realizar backup de todo el FTP. ");
		$zip->close();
		if ($password!=""){
			try{
				system('zip -P '.$password.' '.secured($currentArchive).' '.$currentArchive);
				unlink($currentArchive);
			}catch(Exception $ex){
			
			}
		}
		
		if ($password=="" ){
			show( "El fichero no tiene clave, llama al script pasando el parametro ?pass=xxxx para mayor seguridad" );
		}
		if ($password!=""){$enlaces=secured($enlaces);}
		echo "<br>";
		if($enlaces){
			foreach($enlaces as $enlace){
				if (file_exists($enlace)){
					echo "<a href='".$enlace."'>".$enlace."</a><br>";
				}else{
					echo "No se pudo generar el fichero $enlace <br>";
				}
			}
		}
	}
?>

(*) Como la clase ZipArchive de php no soporta clave, se genera un segundo zip mediante llamada al sistema operativo que si que pone contraseña al original y lo borra.

Nociones de economia

Llevo unos días con ganas de aprender algo de economía con la intención de invertir.

Por lo que he leido términos totalmente desconocidos para mi como pueden ser commodities, forwards, los swaps, las OTC, tipos de derivados financieros o los CFDs entre otros.

Entonces esto no parece un tema sencillo, por lo que vamos a intentar ir por partes:

Macroeconomía vs. Microeconomía.

La Macroeconomía estudia el funcionamiento económico a gran escala, así como la repercusión de las políticas económicas en un país, por ejemplo. Se ocupa de analizar:

– La evolución en el corto plazo de la producción, del empleo y de los precios, es decir, de los ciclos económicos.

– La evolución en el largo plazo de la producción y del nivel de vida, es decir, del crecimiento económico.

La Microeconomía estudia el comportamiento de las unidades económicas individuales: individuos, familias, empresas y los mercados en los que operan. Analiza:

– La formación de los precios a partir del cruce entre Oferta y Demanda.

– La asignación de los recursos escasos entre finalidades alternativas.

Hay varias premisas que tenemos que tener clara desde el comienzo:
– Toda inversión tiene un riesgo ( que nadie intente venderte un producto con riesgo 0 )
– Cuanto menor es el riesgo menor será la rentabilidad, o visto de otra forma, cuanto mayor sea el riesgo, mayor tiene a ser su beneficio, aunque también la posibilidad de perder mucho o todo.

Cuando hablamos de riesgo tenemos que ser conscientes de que se puede perder una parte , todo y con algunos productos se puede perder mas de lo invertido.

Otro de los puntos a tener en cuenta es el tiempo que vas a tener la inversión bloqueada, con la imposibilidad de utilizar ese capital. Por lo que necesitas un remanente de dinero con el que no puedes contar en el tiempo que vas a invertir.

Productos de bajo riesgo
Aquí se incluyen los depósitos y las cuentas bancarias de plazo fijo, que a dia de hoy tienen unos intereses muy bajos, por ejemplo ( y según esta web), el valor mas alto es 2% .

Con esto la ganancia que se consigue es bastante baja si tenemos en cuenta que el coste de la vida crece mientras tenemos el dinero bloqueado.

Otro de los productos es la deuda pública, que en el momento de escribir esto tiene valores incluso negativos (web), por lo que no solo no ganamos dinero, sino que nos cuesta.

Productos de alto riesgo
Comprar deuda privada, es el equivalente al punto anterior, pero cambiando la administración por una empresa. Esto tiene un riesgo mayor, pues la empresa puede quebrar y al final quedarnos sin el dinero. Pero también es posible ganar mas dinero si al final todo sale bien.

Otro de los productos que podemos encontrar como de alto riesgo son las acciones. Que son participaciones del capital de una empresa. Si compramos acciones al final lo que obtenemos es una pequeña ( en algunos casos minúscula) parte de la empresa. En este punto podemos hacer referencia a la bolsa y ver unos de los indicadores principales ( que no el único) que es el IBEX

Uno de los ejemplos que mas me gustan es el de las acciones de amazon:

Donde en el año 1997 las acciones valían 1,5 dolares, para pasar en el año 2007 a cerrar con un valor de 97, que podríamos decir que es muy grande, pero es que si lo comparamos con el valor de 2017, donde han pasado la barrera de los 1000 dolares, es casi irrisorio, pero cuantos de nosotros en el año 1997 podríamos pensar en lo que llegaría a ser….

Como caso contrario podemos mostrar el del banco popular,

Donde la gente que invirtiera en 2008 ha visto como sus acciones han pasado de 25 a 0, por lo que han perdido todo su dinero.

Raiola manda y no el panda

Esta mañana repasando los rss de las distintas web que sigo me ha llamado la atención que en varias paginas hablaban de raiola manda y no el panda, pero ¿ Qué es raiola manda y no el panda?

Pues básicamente un concurso SEO organizado por David Ayala y Deam Romero.
Podeis encontrar el enlace aqui

Pero eso tampoco cuenta nada, por lo que os explico un poco mas.

Todo empezó aproximadamente hace un mes, David & Dean, presentaron mediante un vídeo hace aproximadamente un mes, concretamente el 8 de mayo, lo que iba a acontecer durante el mes de junio, este gran concurso SEO al que pusieron el nombre raiola manda y no panda, y en el que ponían todos sus esfuerzos para que todos los SEOs pudiésemos ponernos a prueba, y lo hacían mediante el siguiente vídeo:

Aquí ya nos dejaron con las ganas a todos, y estábamos deseando que llegará el 1 de junio para empezar a enfrentarnos al concurso SEO de los 5.000. ¡ahí es nada!

Actualmente el concurso lo ganara quien tenga más autoridad, es decir si hoy busco en Google La Raiola manda y no el panda obviamente van a salir sitios que tienen mucho tiempo online y por ende con autoridad, por lo que es complicado que un dominio nuevo pueda hacerles frente.

Es súper interesante como se puede sacar mucho provecho de estos concursos, una de las fuentes de más valor son las oportunidades de en laces que podemos conseguir con el concurso de La Raiola manda y no el panda muchos usuarios sacan esos enlaces “escondidillos” permitiendo que pueda hacerme de una base de datos nueva de sitios donde dejar enlaces para futuros proyectos.

Por qué quieren dar 5000 euros a un ganador

Es fácil. Los packs SEO han dañado el sector y cada vez vemos más anuncios en adwords anunciando top 1 en keywords sin competencia como hoteles, etc.. por sólo 50 euros. Y esto ha hecho mucho daño.

Los SEO que antes cobraban 500 euros por una campaña SEO (al mes), han acabado comprando en Primark por culpa de este nuevo tipo de SEOs. Los SEO destroyers les llaman.

Debido a esta cantidad de bajada en los presupuestos, la compañia Raiola ha decidido donar 5000 euros al sector SEO para que alguien pueda irse de vacaciones este año.

Por otro lado el sector del SEM se ha quejado de la iniciativa. “Nunca hacéis un concurso para nosotros, estamos hasta los huevos y nos posicionamos más rápido” dicen…

¿Estamos ante la próxima guerra de sectores? ¿El Taxi y Cabify/Uber pasarán a un segundo plano? Si los SEO y los SEM quieren, sí. Sin duda.

Y el panda que tiene que ver en esto
El panda ha sido usado como parte de la campaña para causar ternura y facilitar que la gente caiga y participe. El panda siempre ha sido un animal adorable hasta que Google lo uso para nombrar a su algoritmo y desde entonces la gente le tiene manía. Las búsquedas en Youtube de videos sobre panda han bajado, les han cambiado el bambú por cebollinos y ya no saben que hacer con su vida. Están tristes y cabreados.

Ignorar ficheros y directorios en SVN

Crea un fichero de texto donde indicas los ficheros y carpetas que quieres que SVN no tenga en cuenta cuando compara el respositorio. (ignore.txt)

.classpath
.project
target
bin
build

ahora establecemos la propiedad ignore mediante fichero

svn propset svn:ignore -F ignore.txt directorioproyectoquequieresignorar

Una vez establecida la propiedad podemos borrar el fichero.

Patron observer en un comando JONIC desde Bean

En la estructura de JONIC, no podemos crear el comando que extienda de Observable (porque ya extiende de ResultCommand), por lo que creamos una clase interna que si lo haga.

 

public class ConsultaMasivaCMD extends ResultCommand&lt;List&gt;

public PorcentajeMasivoObservable myobs = new PorcentajeMasivoObservable();

public class PorcentajeMasivoObservable extends Observable
{
private int porcentaje = 0;
public int getPorcentaje()
{
return porcentaje;
}
public void setPorcentaje(int porcentaje)
{
this.porcentaje=porcentaje;
setChanged();
notifyObservers();
}

}

 

Y dentro de nuestro comando en el runcommand:

@Override
public ResultCommand&lt;List&lt;Resultado&gt;&gt; runCommand() throws Exception {

      for (int i = 0; i &lt;=array.size() - 1; i++) {

          ... 
          myobs.setPorcentaje(porcentaje);
      }

}

Ahora que ya tenemos la clase observable nos vamos al bean:

public class ConsultaFicherosBean extends RequestAbstractBean implements Serializable, Observer {

private ConsultaMasivaCMD.PorcentajeMasivoObservable pmo =null;

private Integer progress;

public String acConsultar() {

ConsultaMasivaCMD cmd = (ConsultaMasivaCMD)createCommand(ConsultaMasivaCMD.class);
cmd.setObj(this);
pmo=cmd.myobs;
pmo.addObserver(this);
cmd = (ConsultaMasivaCMD) runCommand(cmd);
if (cmd != null &amp;&amp; cmd.getResult() != null)
result.addAll(cmd.getResult());

return "donde sea";
}

@Override
public void update(Observable o, Object arg) {
if (o==pmo){
progress=pmo.getPorcentaje();
}

}

En el xhtml ponemos una barra de progreso con un poll para indicar el estado del progreso.

&lt;h:form id="dataForm"&gt;

&lt;p:commandButton id="viewButton" value="Enviar Petición"
action="#{consultaFicherosBean.acConsultar}"

onclick="PF('pb').start();PF('poll').start();"
onstart="PF('dlg').show()"
oncomplete="PF('dlg').hide();PF('pb').cancel();PF('poll').stop();"
update=":dataForm :prg"

icon="ui-icon-pin-s" /&gt;

&lt;/h:form&gt;

&lt;p:dialog widgetVar="dlg"
appendTo="@(body)"
modal="true"
width="400"
height="100"
header="Progreso"
draggable="false"
closable="false"
closeOnEscape="false"
resizable="false"
minimizable="false"&gt;
&lt;h:form id="prg"&gt;

  &lt;p:poll interval="1"
  widgetVar="poll"
  autoStart="false"
  async="true"
  update=":prg"
  immediate="true"/&gt;

&lt;!-- progressbar--&gt;
&lt;p:progressBar widgetVar="pb"
ajax="true"
value="#{consultaFicherosBean.progress}"
labelTemplate="{value}%"
styleClass="animated"
global="false"&gt;
&lt;/p:progressBar&gt;
&lt;/h:form&gt;
&lt;/p:dialog&gt;

Con esto el comando avisa en cada iteración del porcentaje, el Bean se entera y actualiza su variable de progreso y la página mediante el poll y la barra de progreso puede informar al usuario de como va la tarea.

Corregir fallo prestashop 1.7 carousel

Una vez que he instalado prestashop 1.7 y accedo a la tienda veo que el componente carousel que viene con el tema por defecto no tiene enlace

Aunque en la parte de administración veo que si que está definido:

 

Para arreglarlo es necesario modificar el fichero:

themes/classic/modules/ps_imageslider/views/templates/hook/slider.tpl

Añadiendo el elemento a, dentro de figure:

Una vez hecho, es necesario vaciar la caché para que podamos ver el resultado: