Mostrando entradas con la etiqueta jquery. Mostrar todas las entradas
Mostrando entradas con la etiqueta jquery. Mostrar todas las entradas

sábado, 15 de junio de 2013

Insertar en multiples webs el aviso legal del uso de cookies

Enunciado

Alojas en uno mas servidores sitios web que utilizan Google Analytics (GA) para el seguimiento de visitas.
Dado que GA utiliza cookies para el seguimiento del usuario, estas obligado por el artículo 4 del Real Decreto Ley 13/2012 a visualizar una advertencia del uso estas cookies y permitir su eliminación.
Pero:
  • Son varios sitios web.
  • Algunos no los has desarrollado tú, ni llevas su mantenimiento.
  • Utilizan distintas técnicas de servidor para llamar al código GA.
¿Cómo visualizas el panel con mínimo esfuerzo y perjuicio de los sitios?
Contexto
IIS 7.5 c/ URL-Rewrite module 2.0 instalado.

Prefacio sobre la ley

Antes de presentar mi solución quiero comentar los aspectos legales de este panel. No soy abogado y puedo equivocarme muy facilmente, si tienes dudas asegúrate con expertos.

Sobre la obligatoriedad de visualizar el panel citado existen diversas opiniones,incluso mucha gente opina que no es obligatorio. Al fin y al cabo estas cookies no almacenan información personal.

Pero si consultas la Guia sobre el uso de las cookies que ha publicado la Agencia Española de Protección de Datos en la página 8, en el tipo de Cookie de Análisis (que son la de Google) leeras:

Cookies de análisis: Son aquéllas que permiten al responsable de las mismas, el seguimiento y análisis del comportamiento de los usuarios de los sitios web a los que están vinculadas. La información recogida mediante este tipo de cookies se utiliza en la medición de la actividad de los sitios web, aplicación o plataforma y para la elaboración de perfiles de navegación de los usuarios de dichos sitios, aplicaciones y plataformas, con el fin de introducir mejoras en función del análisis de los datos de uso que hacen los usuarios del servicio.
Respecto al tratamiento de datos recabados a través de las cookies de análisis, el grupo de trabajo del articulo 29 ha manifestado que, a pesar de que no están exentas del deber de obtener un consentimiento informado para su uso, es poco probable que representen un riesgo para la privacidad de los usuarios siempre que se trate de cookies de primera parte, que traten datos agregados con una finalidad estrictamente estadística, que se facilite información sobre sus uso y se incluya la posibilidad de que los usuarios manifiesten su negativa sobre su utilización

Yo interpreto de este texto que es obligatorio un panel que informe del uso de cookies y permita desactivarlas. Otra cosa es que el número de sitios que violen la legalidad sea tan grande y el delito tan pequeño que la mayoría se libren de la sanción.

Otro tema a discutir es el contenido, permanencia y flujo del panel.

En mi caso visualizo el panel al principio de cada sesión, informo y doy la oportunidad de desactivar las cookies. Si pasan a otra página el panel ya no aparece. En cualquier caso el panel les informa de que pueden volver a activar o desactivar las cookies accediendo a la política de privacidad del sitio.

Dos sitios para leer sobre este tema son: Analítica Web o LetsLaw

Solución técnica

Resumiendo lo que vamos a hacer:
La primera vez que un usuario acceda a alguna página de alguno de nuestros sitios web aparecerá en la parte superior un panel informativo que:
  • Informará del uso de cookies analíticas que respentan la privacidad.
  • Permitirá deshabilitar estas cookies.
Por ejemplo:
Uso de cookies: Según la legislación española tiene la posibilidad de desactivar las cookies analíticas. Usamos estas cookies para mejorar nuestros servicios, respetando su privacidad y sin almacenar datos personales. Para recuperar este panel acceda a la política de privacidad del sitio.

Este panel se visualizará solo al visualizar la primera página. El efecto de deshabilitar las cookies será borrarlas  inmediatamente y luego cada vez que se cargue una página del sitio. El borrado será selectivo, respetando las cookies técnicas.

Lo importante de nuestra solución es que no modificaremos ninguna página directamente,  usaremos el modulo url-Rewrite de IIS7.5  para  inyectar en la página código JavaScript almacenado en un punto común.

Es una solución similar pero independiente a la que mostré en un post anterior Insertar código de seguimiento Google Analytics sin editar todas las páginas, ambas soluciones son compatibles pero no se requieren.

Veamos primero la regla de salida (outbound-rule) del modulo url-rewrite tal como se almacena en el Web.config de cada sitio:

   
       
            
            
      
    
    
        
             
             
        
        

La regla busca la etiqueta </head> y le antepone el siguiente código:
<link href="http://www.miHost.es/LSSI/CookiesPolicy.css" rel="stylesheet" type="text/css">
<script src="http://code.jquery.com/jquery-1.10.1.min.js" type="text/javascript">
<script src="http://www.miHost.es/LSSI/jquery.cookies.2.2.0.min.js" type="text/javascript">
<script src="http://www.miHost.es/LSSI/CookiesPolicy.js" type="text/javascript">

Dado que la etiqueta </head> aparece frecuentemente en código java generado por ASP.net, la regla utiliza una pre-condición (a continuación de la definición) para que se aplique solo al contenido HTML. Esto se consigue con dos requisitos:
  • Exigiendo que el REQUEST-CONTENT-TYPE sea text/html
  • Evitando cargar archivos en cuyo nombre (REQUEST-FILE-NAME) aparezca la extensión .ashx
Según tu entorno es posible que tengas que añadir mas pre-condiciones, vigila la consola de java por si salen errores.

La página reescrita cargará los siguientes archivos externos:
  • Un hoja de estilo que se aplicará al panel.
  • La librería jQuery
  • La librería de gestión de cookies disponible aquí.
  • Una librería en javaScript que constituye el núcleo de la solución que os presento mas abajo.
Antes de continuar observa que:
  • La libreria jQuery se carga del repositorio CDN
  • El CSS y las otras dos librerías se cargan de un sitio central  http://www.miHost.es/LSSI que unifica el mantenimiento.
Ese sitio central debe ser tuyo y deberás editar esta parte de la regla para señala al sitio donde almacenas las librerías.

Vamos a ver la librería nuclear:  "cookiesPolicy.js"
$(document).ready(function () {
    if ($.cookies.test() ) { /*si el navegaro no hacepta cookies para que molestarse*/
        var flagCookies = $.cookies.get("banderaCookies");
        if (flagCookies == null) {  /*hay que mostrar el panel */
            $.cookies.set("banderaCookies", "SI");
            genAvisoCookies();
        } else { /*Borramos las cookies que se han creado hasta el momento */
            if ($.cookies.get("banderaCookies") == "NO") {
                borraCookies();
            }
        }
    }; 
});
function borraCookies() {
    /*Borra las cookies de Google y una mia*/
    var dominio = '.' + /\w*\.\w*$/.exec(window.location.host)[0];
    $.cookies.del("__utma", { path: '/', domain: dominio });
    $.cookies.del("__utmb", { path: '/', domain: dominio });
    $.cookies.del("__utmc", { path: '/', domain: dominio });
    $.cookies.del("__utmz", { path: '/', domain: dominio });
    $.cookies.del("__utmv", { path: '/', domain: dominio });
    $.cookies.del("idCart", { path: '/', domain: null });
}
function genAvisoCookies() 
/*Añade un div centrado justo despues de la etiqueta body*/
{
    $("body").prepend("
" + textoCookies() + "
"); $("#desactivarCookies").click(function (e) { $.cookies.set("banderaCookies", "NO"); borraCookies(); window.location.reload(false); }) } function textoCookies() /*implantación de bi-idioma para el panel */ /*Se considera tanto el idioma del navegador como la posibilidad de un parametro lang en el query string*/ { var lang = navigator.language || navigator.userLanguage var param = QueryString("lang"); var texto if ((param=="en-GB") || ((param==null) && !(/es/.test(lang)))) { texto = "Cookies usage:"; texto += " According to Spanish law you have the option of"; texto += " disable analytical cookies." texto += " We use these cookies to improve our services, respecting your privacy and without storing personal data."; texto += " Access the privacy policy to recover this panel."; } else { texto = "Uso de cookies: "; texto += "Según la legislación española tiene la posibilidad de"; texto += " desactivar las cookies analíticas."; texto += " Usamos estas cookies para mejorar nuestros servicios, respetando su privacidad y sin almacenar datos personales."; texto += " Para recuperar este panel acceda a la política de privacidad del sitio."; } return texto } function QueryString(sParam) { /*Funcion de utilidad para extraer el query string*/ var sPageURL = window.location.search.substring(1); var sURLVariables = sPageURL.split("&"); for (var i = 0; i < sURLVariables.length; i++) { var sParameterName = sURLVariables[i].split("="); if (sParameterName[0] == sParam) { return sParameterName[1]; }; } } /*Las funciones siguientes se utilizan en enlaces del documento de politica de privacidad Para volver a activar las cookies o desactivarlas independientemente del panel*/ function activaCookies() { $.cookies.set("banderaCookies", "SI"); if (!$('#centradoAC').length) { genAvisoCookies(); } goToByScroll(); } function desactivarCookies() { $.cookies.set("banderaCookies", "NO"); borraCookies(); if (!$('#centradoAC').length) { genAvisoCookies(); } goToByScroll(); } function goToByScroll() { /*efecto de animación para ir a la parte superior donde se visualiza el panel*/ $('html,body').animate({ scrollTop: $('#centradoAC').offset().top }, 'slow'); }
Antes de explicar esto te advierto una cosa, no soy un experto en javaScript ni jQuery, consigo que mi código funcione pero puede optimizarse, ... y explicarse mejor. Comencemos:

Vemos que al principio...
$(document).ready(function () {
    if ($.cookies.test() ) { /*si el navegaro no hacepta cookies para que molestarse*/
        var flagCookies = $.cookies.get("banderaCookies");
        if (flagCookies == null) {  /*hay que mostrar el panel */
            $.cookies.set("banderaCookies", "SI");
            genAvisoCookies();
        } else { /*Borramos las cookies que se han creado hasta el momento */
            if ($.cookies.get("banderaCookies") == "NO") {
                borraCookies();
            }
        }
    }; 
});
... estamos enlazando el evento documento cargado (jquery document ready) a una función que según convenga puede hacer tres cosas:
  • Nada (si el navegador no soporta cookies, ¿para que molestarse?)
  • Visualizar el panel requerido por la ley (si es la primera vez en esta sesión que accede al sitio)
  • Borra las cookies (si durante esta sesión el usuario ha dicho que no quiere cookies de análisis)
Usamos una cookie de sesion "banderaCookies" que puede valer null, 'SI', 'NO'

La siguiente parte del código...
function borraCookies() {
    /*Borra las cookies de Google y una mia*/
    var dominio = '.' + /\w*\.\w*$/.exec(window.location.host)[0];
    $.cookies.del("__utma", { path: '/', domain: dominio });
    $.cookies.del("__utmb", { path: '/', domain: dominio });
    $.cookies.del("__utmc", { path: '/', domain: dominio });
    $.cookies.del("__utmz", { path: '/', domain: dominio });
    $.cookies.del("__utmv", { path: '/', domain: dominio });
    $.cookies.del("idCart", { path: '/', domain: null });
}
...corresponde al borrado de las cookies. En este código borramos las cookies de Google y una propia insertada desde código.
Si tus desarrolladores usan otras cookies deberas incluirlas aquí (cuidado con el dominio y el path, puede darte problemas).

A continuación tenemos la función se ocupa de insertar el panel:
function genAvisoCookies() 
/*Añade un div centrado justo despues de la etiqueta body*/
{
    $("body").prepend("
" + textoCookies() + "
"); $("#desactivarCookies").click(function (e) { $.cookies.set("banderaCookies", "NO"); borraCookies(); window.location.reload(false); }) }
Vemos que tiene dos partes:
  1. Insertar unos marcos (<div>) con el texto del panel
  2. Enlazar una función a un elemento #desactivarCookies que veremos mas adelante
Para el centrado y maquetado del texto se hace referencia a estilos CSS definidas en el archivo: CookiesPolicy.css.
En mi caso bastaron estos:
#centradoAC {width:100%;text-align:center;}
#marcoAC
{
    background-color: white;
    text-align: left;
    width: 920px;
    margin: 0 auto;
    padding-bottom: 10px;
    font-family: Arial;
    color: #999999;
    font-size: 13px;
    font-weight: lighter;
}
#marcoAC strong {font-weight: bold;}
#marcoAC a:link
{
    color: #999999;
    text-decoration: underline;
    font-weight:normal;
}
#marcoAC a:visited
{
    color: #999999;
    text-decoration: underline;
    font-weight:normal;
}
#marcoAC a:link
{
    text-decoration: underline;
    font-weight:normal;
}
#marcoAC a:visited
{
    color: #999999;
    text-decoration: underline;
    font-weight:normal;
}
#marcoAC a:hover
{
    color: #999999;
    text-decoration: underline;
    font-weight:bold;
}
(Nuevamente, no soy un experto en nada, tampoco en CSS)
Según la variedad y modernidad de tus diseñadores webs es posible que tengas que alterar mas o menos estas clases. ¡Suerte!.
En mi caso basta que el panel se vea así:
Con un ancho de 900px que se adapta bien a diseños no adaptativos (valga la redundancia).
Siguiendo con el código javaScript, tenemos la generación del texto dentro del panel en dos idiomas:

function textoCookies()
/*implantación de bi-idioma para el panel */
/*Se considera tanto el idioma del navegador como la posibilidad de un parametro lang en el query string*/
{
var lang = navigator.language || navigator.userLanguage
var param = QueryString("lang");
var texto
if ((param=="en-GB") || ((param==null) && !(/es/.test(lang)))) {
    texto = "Cookies usage:";
    texto += " According to Spanish law you have the option of";
    texto += " disable analytical cookies."
    texto += " We use these cookies to improve our services, respecting your privacy and without storing personal data.";
    texto += " Access the privacy policy to recover this panel.";
 
}
else {
    texto = "Uso de cookies: ";
    texto += "Según la legislación española tiene la posibilidad de";
    texto += " desactivar las cookies analíticas.";
    texto += " Usamos estas cookies para mejorar nuestros servicios, respetando su privacidad y sin almacenar datos personales.";
    texto += " Para recuperar este panel acceda a la política de privacidad del sitio.";
}
    return texto
}

function QueryString(sParam) {
/*Funcion de utilidad para extraer el query string*/
    var sPageURL = window.location.search.substring(1);
    var sURLVariables = sPageURL.split("&");
    for (var i = 0; i < sURLVariables.length; i++) 
    {
        var sParameterName = sURLVariables[i].split("=");
        if (sParameterName[0] == sParam) 
        {
            return sParameterName[1];
        };
    }
}
Aquí puedes tener variantes (la mas sencilla visualizarlo siempre en español). En mi caso el idioma en la mayor parte de webs viene especificado por un parámetro "lang" del query string. Por si este parámetro está ausente incluyo también el idioma del navegador.

Hasta aquí la visualización inicial del panel.  Pero tambien es conveniente que la política de privacidad del sitio incluya a su vez opciones para activar o desactivar las cookies de análisis.
Por ejemplo:
Si lo desea puede:


  1. Desactivar las cookies de análisis.
  2. Activar las cookies de análisis.

Los editores wysiwig de los gestores de contenido permiten insertar normalmente estos enlaces con referencia a las funciones javaScript siguientes.
/*Las funciones siguientes se utilizan en enlaces del documento de politica de privacidad
Para volver a activar las cookies o desactivarlas independientemente del panel*/
function activaCookies() {
    $.cookies.set("banderaCookies", "SI");
    if (!$('#centradoAC').length) {
        genAvisoCookies();
    }
    goToByScroll();
}

function desactivarCookies() {
    $.cookies.set("banderaCookies", "NO");
    borraCookies();
    if (!$('#centradoAC').length) {
        genAvisoCookies();
    }
    goToByScroll();

}

function goToByScroll() {
/*efecto de animación para ir a la parte superior donde se visualiza el panel*/
    $('html,body').animate({ scrollTop: $('#centradoAC').offset().top }, 'slow');
}

Al situar

Curiosidades

Es curioso que en el momento de redactar el sitio web del gobierno  utiliza cookies de análisis de GA pero no visualiza el panel. ¡ En cierta medida el gobierno viola la ley que el mismo aprobó!
Actualización:
No violan la ley, ¡se excluyeron de la ley!. Bueno al menos en los últimos tiempos han incluido el aviso de cookies y hacen lo que todos.

Todo este trabajo se habría simplificado si la ley hubiera excluido claramente este tipo de cookies. Esto es lo que ha hecho el gobierno francés en su transposición de la directiva europea.

Por que todo esto cacao lo provoca  la Directiva 2009/136/CE. que endureció la Directiva 2002/58/CE
¿No tienen otra cosa que hacer nuestros burócratas europeos?  Por ejemplo, resolver la crisis económica de la zona Euro.

miércoles, 23 de enero de 2013

Problemas con jquery 1.9 en visual studio 2012

Síntomas:

Actualizas la versión de jQuery a 1.9.0 con el administrador de paquetes.
Tus aplicaciones dejan de funcionar incluso aunque actualices el resto de paquetes con dependencias de jQuery.
En mi caso dejo de funcionar el entorno jquery.mobile

Solución:

Vuelve a la versión 1.8.3 de jQuery utilizando la consola de Powershell de VS2012
Dado que hay diversos paquetes que dependen de jQuery te será comodo un powershell script.
Yo me cree este:
Uninstall-Package jquery.mobile
Uninstall-Package jQuery.UI.Combined
Uninstall-Package Microsoft.jQuery.Unobtrusive.Ajax
uninstall-package Microsoft.jQuery.Unobtrusive.Validation
Uninstall-Package jQuery.Validation 

Uninstall-Package jquery
install-Package jquery -Version 1.8.3

install-Package jquery.mobile
install-Package jQuery.UI.Combined
install-Package jQuery.Validation 
install-package Microsoft.jQuery.Unobtrusive.Validation
install-Package Microsoft.jQuery.Unobtrusive.Ajax

Dependiendo de los paquetes que tengas instalados pueden salir errores, solo tienes que ir añadiendo los paquetes al script. Las dependencias también te pueden crear problemas, todo es cambiar el orden.

Comentario:

Con el tiempo se resolverá pero es interesante conocer como revertir Nuget.

viernes, 6 de julio de 2012

Autocompletar un cuadro de texto en jQuery Mobile

Enunciado:

Tenemos la clásica búsqueda con un cuadro de texto al que normalmente desplegaríamos un lista de sugerencias a medida que escriben. En una aplicación de escritorio ASP.NET usariamos el componente autocomplete de AJAX.NET o de jQuery UI. Pero ¿En una aplicación móvil?

Solución:

Usar jQuery y el componente jQM Autocomplete de Andy Matthews.
Si haces clic en el vínculo llegaras a una demo y te enamoraras como lo hice yo.

Desarrollo de la solución

Necesitaras un servicio JSON que te entregue las sugerencias de palabras. Si desarrollas en ASP.NET puedes consultar mi post Desarrollo de un servicio json sobre ASP.NET para consumo desde Jquery
En mi caso además estaba buscando nombres de empresa en una base de datos por lo que dispongo del identificador único de registro.
Por eso devuelvo un array de objetos con dos propiedades de texto "label" y "value" .
Esta es la respuesta JSON:

[{"label":"INCOAZUL","value":"353"},{"label":"AZULIBER","value":"18"},{"label":"AZULEJERA ALCORENSE","value":"62"},{"label":"MAYOLICA AZULEJOS, S.L.","value":"60"},{"label":"AZULEJOS ALCOR","value":"61",{"label":"AZULINDUS & MARTI, S.A.","value":"13"},{"label":"NOMAZUL, S.A.","value":"85"}}] Este un ejemplo código de página:

    Que completaríamos con esté código JavaScript
           $(document).ready( function (e) {
             $("#busqueda").autocomplete({
                 method: 'GET',
                 target: $('#sugerencias'),
                 source: "/MAC.svc/NomIdEmpresa",
                 link: 'ficha?id=',
                 minLength: 3
             });
         });
     
    
    Por supuesto en los encabezados debes incluir las referencias a las librerias y estilos jQuery, jQuery Mobile y jQM autocomplete.
    Observarás que en la parte java especifico un enlace ficha?id= que se completará al final con el campo value que me devuelve el JSON. Asi cuando clicken en INFOAZUL navegaremos hacia ficha?id=353.
    Sin embargo, esta no es la solución optima, lo mejor sería cambiar a una página móvil ya cargada y actualizarla con los datos leidos con un llamada a un servicio JSON datosEmpresa(idEmpresa). 
    Es mi próximo objetivo, así que....continuará en próximas entregas

    Desarrollo de un servicio json sobre ASP.NET para consumo desde Jquery

    Enunciado:

    Necesitamos un servicio web que responda en formato JSON para poderlo leer desde Jquery.

    En este ejemplo partiremos del resultado otro servicio web WCF "directorio", pero podríamos estar devolviendo los resultados de una consulta linq o cualquier otra fuente serializable

    Solución

    Añadir un Servicio WCF con AJAX habilitado.
    IMPORTANTE: En el Web.config sección system.servicemodel ELIMINAR o comentar las referencias a este servicio  . En mi caso que el servicio se llamaba MAC eliminé las líneas: el behavior  enablewebScript
    
    
    
          
            
                 
                
          
          
        
        
        
          
            
          
        
    

    Luego editamos el archivo de etiquetado (.svc) añadiendo el atributo Factory apundando a la clase System.ServiceModel.Activation.WebServiceHostFactory.
    <%@ ServiceHost Language="VB" Debug="true" Service="myAplication.MAC" CodeBehind="MAC.svc.vb" Factory="System.ServiceModel.Activation.WebServiceHostFactory" %>
    
    En el archivo de código MAC.svc.vb definiremos los metodos con el atributo WebInvoke especificando el método (GET o POST) y el formato de la respuesta (JSON).
    Imports System.ServiceModel
    Imports System.ServiceModel.Activation
    Imports System.ServiceModel.Web
    
    
    
    Public Class MAC
    
        
        
        Public Function NomIdEmpresa(term As String) As directorio.EmpresaIdentificada()
            'ahora llamo a la clase proxy de mi servicio WCF.
            Dim servicio = New directorio.IdirectorioClient
            Dim resultado As directorio.EmpresaIdentificada() = servicio.SugerenciasNombreIdEmpresa(term, 20) 
            servicio.Close()
            Return resultado
        End Function
    End Class
    

    viernes, 27 de enero de 2012

    No funciona jquery.mobile.pagination

    Sintomas:
    Los vinculos laterales en  el plugin de Filament Group para paginación en jquery-mobile no pasan página. En cambio si funciona arrastrandon el dedo por la página actual.

    Causa:
    Es un defecto del plugin relacionado con la url. En la página advierten que puede ocurrir con algunos navegadores, pero he observado  que el problema puede aparecer o desaparecer con variaciones en la URL.
    Por ejemplo en un momento creí lo siguiente, pero luego el problema reapareció. Sigo trabajando
    En los href especificaba la dirección completa incluyendo el número de puerto en el Host.
    Ejemplo href="http://www.blogger.com:80/nexpost.html"
    Basto con eliminar el :80 para que todo funcionara.

    Comentarios:
    Perdí un día por esta tontería.
    El problema es que en mi desarrollo Encuentra tu cerámica la clase que contiene los metodos que extraen la información de las urls, almacena esta información y genera todas las urls del sitio ha terminado por ser bastante complejo, en algún sitio se me coló un request.url.originalString en lugar de un request.url.absoluteUrl.
    En fin.
    No estoy seguro de que el problema esté en especificar el puerto únicamente o en especificar el puerto cuando se ha accedido sin especificar el puerto originalmente (urls distintas).