<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
<channel>
<title>Noticias</title>
<link>https://betabeers.com/news/feed/</link>
<description>Noticias</description>
<lastBuildDate>Fri, 06 Mar 2026 09:00:53 +0000</lastBuildDate>
<language>es</language><item>
<title><![CDATA[Entrevista a Jacin Fleta - Transparente y ejecutor]]></title>
<link>https://betabeers.com/blog/entrevista-a-jacin-fleta-transparente-ejecutor-409/</link>
<pubDate>Mon, 28 Dec 2020 11:43:40 +0000</pubDate>
<category>Entrevistas</category>
<content:encoded><![CDATA[<p><img src="https://betabeers.com/uploads/blog/20201228_jacin_fleta.png" alt="Entrevista a Jacin Fleta - Transparente y ejecutor" class="img_post" target="_blank" rel="nofollow" /></p>
<p>Hace poco que sigo en twitter a <a href="https://twitter.com/jacintofleta" target="_blank" rel="nofollow">Jacin Fleta</a>, me parece una persona que te puede inspirar y motivar a partes iguales, creo que si te gusta el marketing querrás aprender a programar y si eres del lado de la programación querrás saber como hacer marketing, te recomiendo que le sigas en twitter y su sideproject <a href="https://checkmypresets.com/" target="_blank" rel="nofollow">Check my Presets</a>.</p>
<p>[toc]</p>
<h2>Personal</h2>
<p><strong>¿A qué edad y cómo empezaste a programar? ¿Te motivó alguna persona, película….?</strong></p>
<p>Estudié Ingeniería industrial y en una asignatura nos enseñaron a programar en C (Tendría 20 años aprox.) Fue la única asignatura de toda la carrera que me gustó. Pero la vida me llevó por otro camino y me acabé dedicando al marketing en mi propia empresa.</p>
<p>No volví a tocar la programación hasta los 27 años (ahora tengo 28). Aproveché la cuarentena para sumergirme en aprender y me encantó. He encontrado una de mis pasiones.</p>
<p>Decidí aprovechar la cuarentena porque durante varios meses me relacioné con varios programadores y veía que lo que hacían era tangible. Parece una tontería pero muchas veces lanzas una campaña de marketing y no sabes cual va a ser su resultado. Sin embargo, un programa o funciona o no funciona. Eso me encantó.</p>
<p>Otra cosa que me motivó fue la posibilidad de juntar lo que había aprendido de marketing como CMO de <a href="https://flamingueo.com/" target="_blank" rel="nofollow">Flamingueo</a> con habilidades técnicas.</p>
<p><strong>¿Alguién de tu círculo cercano como pueden ser familiares o amigos saben que es lo que haces?</strong></p>
<p>Tengo mucha suerte. Mi círculo me apoya mucho. Muchos no saben lo que hago pero aún así noto que me van a apoyar haga lo que haga. </p>
<p><strong>Veo que te ha inspirado bastante levelsio ¿Qué es lo que más te gusta de él y qué intentas aplicar en tu vida y proyectos?</strong></p>
<p>Hablé con un amigo y le comenté que lanzar un proyecto a la semana era lo que más feliz me podía hacer. Fue ahí cuando me dijo: tienes que seguir a levelsio. Y eso hice.</p>
<p>Pienso que para que un proyecto tenga éxito se necesita mucho tiempo y muchas iteraciones. Esto es incompatible con lo de lanzar uno a la semana así que por ahora me conformo con dedicarle casi todo mi tiempo a mi propio proyecto (checkmypresets).</p>
<p>Respecto a levelsio, lo que más me gusta es la independencia y la libertad que transmite. No está atado a nada ni a nadie.</p>
<p><strong>¿Nos puedes hablar de flamingueo? ¿Es verdad que estuviste a punto de abandonar en algún momento? ¿Qué te hizo seguir adelante? ¿Te pusiste un tiempo máximo?</strong></p>
<p>Flamingueo nace en 2016 después de que mi socio Emilio Peña tuviera la loca idea de vender un flotador de flamenco que había visto en cuentas australianas de instagram. Mi otro socio Pablo Niñoles y yo no confiábamos mucho en la idea.</p>
<p>Pero Emilio transmitía mucha pasión y nos enseñó pruebas de por qué pensaba que era una ola que iba a llegar a España. Acertó.</p>
<p>Fuimos muy rápidos ejecutando (tardamos 1 día en lanzar) y fuimos sacando poco a poco productos nuevos. Sin hacer dropshipping ni nada, al principio con nuestras casas llenas de producto y finalmente con almacén. Empezamos pidiendo un préstamo bancario con aval personal para poder empezar.</p>
<p>A día de hoy hemos tenido casi 100k clientes. Para mi las claves han sido: visión de producto, comunicación acertada, velocidad de ejecución y una gran dosis de suerte.</p>
<p>En estos casi 5 años hemos tenido momentos de bajón y momentos de subida. Hemos vivido una auténtica montaña rusa pero imagino que como cualquier start up.</p>
<p>Ni mi socio Pablo ni yo estamos actualmente en el día a día de la empresa. En mi caso, sentía la necesidad de construir cosas nuevas y había una persona en mi equipo de marketing (Diana Terekhova) en la que confiaba para poco a poco ir sustituyéndome.</p>
<p>Ahora Check my Presets es mi proyecto principal.</p>
<p><strong>Tu que eres capaz de hacer marketing y programación ¿Cuál es tu área favorita? ¿Qué es lo que más te gusta de cada una?</strong></p>
<p>En marketing me fascina crear campañas que puedan llegar a millones de personas con muy poco dinero. Me encanta el proceso creativo de construir una historia, transformarla en campaña y lanzarla. En Flamingueo hemos lanzado muchas así.</p>
<p>Ahora me estoy enfocando un poco más en marketing de producto. Esto es intentar construir productos que enganchen, que tengan recurrencia y que sean virales por sí solos. Es muy fácil contarlo y muy difícil hacerlo. Yo con check my presets aún no lo he conseguido.</p>
<p>Respecto a la programación, me ayuda a pensar sin límites. Antes se me ocurría X pero necesitaba a alguien técnico para llevarlo a cabo. Programar me ayuda a tangibilizar ideas.</p>
<p>Me apasiona que otras personas puedan usar algo que yo he creado. Juntar ambas técnicas me da mucha independencia.</p>
<h2>Sideprojects</h2>
<p><strong>¿Qué opinas de los sideprojects? Muchos pensarán que son una pérdida de tiempo pero tu has conseguido ganarte la vida con ellos.</strong></p>
<p>Realmente no me gano la vida con ellos. Ahora mismo estoy viviendo de mis ahorros pero mi idea es poder vivir de ellos a corto plazo. Concretamente de <a href="https://checkmypresets.com/" target="_blank" rel="nofollow">Check my Presets</a>.</p>
<p>Pienso que para aquellas personas que tengan un trabajo estable pero tengan las ganas de probar a emprender sin mucho riesgo un side project es genial. Quizá esos side projects puedas hacerlos crecer y finalmente dedicarte a ellos.</p>
<p>De todas formas, no creo que los side projects se deban enfocar siempre al dinero, también se pueden enfocar como algo muy divertido (a mi juicio) que te ayuda a aprender cosas nuevas.</p>
<p><strong>No hace mucho has lanzado checkmypresets ¿Nos puedes contar de que trata? ¿Cómo se lo vendes a una persona que piensa que puede usar filtros de instagram gratis?</strong></p>
<p>Check my Presets ayuda a fotógrafos a ganar dinero a través de internet vendiendo productos digitales. Actualmente un fotógrafo puede registrarse y vender sus presets (filtros) en minutos. </p>
<p>En instagram hay decenas de filtros gratis y para la mayoría de gente son suficientes. A esas personas les diría que es mejor que no compren en Check my Presets.</p>
<p>Check my Presets es más para usuarios que quieren obtener presets de profesionales que han tocado decenas de ajustes para tipos de fotos concretas. Por ejemplo: una foto de interior donde predomina el blanco. Hay gente a la que le gusta la edición de imagen y quiere tener una colección de ajustes perfectos para cada tipo de foto.</p>
<p>Ese tipo de usuario un poco más pro es perfecto para Check my Presets.</p>
<p><strong>¿A través de qué canal (ej. instagram) y cómo convenciste a las primeras personas para crear su tienda en checkmypresets?</strong></p>
<p>Al principio usé la cuenta de Flamingueo para hablar manualmente a muchos fotógrafos con audiencias grandes. Usé la cuenta de Flamingueo porque pensé que al tener el check azul era más fácil que me leyeran.</p>
<p>De aproximadamente 200 mensajes me contestaron 10 y solo 1 me dijo que quería probar la plataforma. Ese fotógrafo es Marcos Alberca. Gracias a él otros fotógrafos se han ido registrando y prácticamente esa es toda la promoción que he hecho de Check my presets.</p>
<p><strong>Imagino que tuviste el problema del huevo y la gallina, que empezaste sin vendedores y clientes ¿Tardaste mucho en conseguir la primera venta?</strong></p>
<p>Me enfoqué solo en vendedores. Los propios vendedores promocionan su tienda porque quieren ventas y así llegan los clientes. Y no solo clientes, también llegan otros fotógrafos porque se siguen entre ellos.</p>
<p><strong>¿Recuerdas algún cambio o funcionalidad que hayas hecho que hubiera disparado las ventas de un día para otro?</strong></p>
<p>Poner otros creadores recomendados al final de la tienda de un creador subió las ventas ya que aumentó el número de presets diferentes que veía un usuario.</p>
<p>Hoy en día, tras investigar a muchos fotógrafos me di cuenta de que además de ganar dinero vendiendo presets también suelen vender cursos o promocionar su equipo con enlaces de afiliados.</p>
<p>Ahora todo eso está integrado en Check my Presets. De esta forma trato de ser el enlace de su bio. El único enlace que tienen que compartir para ganar dinero.</p>
<p>Te contaré si esto funciona.</p>
<p><strong>¿Qué pasos le recomiendarias a una persona que sepa programar pero todavía no se ha centrado en un sideproject? ¿Qué te hizo decantarte por checkmypresets y no otros proyectos?</strong></p>
<p>Le diría que lo más difícil es decidir empezar. Y que antes de ponerse a programar piense cómo va a conseguir los primeros usuarios y cómo va a hacer que el producto crezca (aunque sea lentamente).</p>
<p>Y que si tiene un trabajo estable solo lo deje si tiene ahorros para aguantar con tiempo sin ingresos. Si no es el caso, que haga el side project en findes o a ratos. </p>
<p>Que un side project funcione es poco probable pero altamente gratificante. Y se aprende muchísimo. </p>
<p>Me decanté por Check my Presets porque validé que muchísimas personas pagaban por presets. Los lanzamos en Flamingueo y vendimos cientos en pocas semanas. Primero vendí y luego programé.</p>
<p><strong>¿Cual es el stack de checkmypresets?</strong></p>
<p>Es el que se conoce como MERN:</p>
<ul>
<li>Front: React en Netlify</li>
<li>Back: Node + Express en Heroku</li>
<li>Alojamiento de imagenes: Cloudinary</li>
<li>Librería CSS: Bootstrap</li>
<li>Base de datos: Mongo DB</li>
</ul>
<p>[lock]</p>
<h2>Tecnologías</h2>
<p><strong>¿Cual es tu stack favorito?</strong></p>
<p>A nivel integral solo conozco el stack MERN</p>
<p>Tambien he hecho mini proyectos web en python con flask y django. Me encantaron pero decidí aprender más sobre MERN.</p>
<p><strong>¿Qué te gusta más bootstrap, tailwind u otro? ¿Por qué?</strong></p>
<p>Tailwind. Lo conocí hace poco (tened en cuenta que soy nuevo jaja) y lo probé en brandedsanta.com (otro side project) y flipé.</p>
<p>Ojalá lo hubiera usando desde el día 1 en check my presets. Ahora me da pereza pasar de Bootstrap a Tailwind.</p>
<p>Y bueno, estoy utilizando React Bootstrap y el código queda muy limpio. No sé si hay una librería de React de Tailwind tan limpia como React Bootstrap.</p>
<p><strong>¿Alguna tecnología o implmentación de alguna o varias existentes que te haya sorprendido?</strong></p>
<p>React me dejó loco. Mi forma de aprender a programar fue la típica de tener CSS, HTML y JavaScript separados. Lancé mini webs así y me gustó pero lo vi tedioso.</p>
<p>Para alguien que acaba de aprender el abc del desarrollo web pasar a React es como ver la luz.</p>
<p>¿Qué webs o canales recomiendas para aprender sobre programación?</p>
<p>Justo escribí un artículo sobre esto: <a href="https://jacin.substack.com/p/cmo-aprender-a-programar-de-una-maldita" rel="nofollow" target="_blank">https://jacin.substack.com/p/cmo-aprender-a-programar-de-una-maldita</a></p>
<p>Recomiendo el curso CS50 de HarvardX gratuito en edx.org</p>
<p>Una vez finalizado, recomiendo especializarse en desarrollo web con el curso CS50w de HarvardX también gratuito en edx.org</p>
<p>Otra plataforma maravillosa y gratuita para aprender es freeCodeCamp.</p>
<h2>Eduación</h2>
<p><strong>¿Cual sería el primer lenguaje de programación que le recomendarías a alguien que quiere aprender a programar?</strong></p>
<p>Primero les preguntaría por qué quieren aprender programación. Si no lo saben les diría que no empiecen ya que no van a ser constantes. </p>
<p>No quieres aprender c++, quieres hacer videojuegos.</p>
<p>No quieres aprender Python, quieres analizar datos.</p>
<p>No quieres aprender Flutter, quieres hacer apps.</p>
<p>No quieres aprender JavaScript, quieres hacer aplicaciones webs.</p>
<p>El por qué es lo más importante.</p>
<p>Si quieres lanzar webs que sean side projects lo más lógico es seguir el proceso HTML -&gt; CSS -&gt; JavaScript -&gt; React o Vue -&gt; Node + Express -&gt; MongoDB</p>
<p>Lo bueno de este camino es que te será fácil luego aprender desarrollo móvil a través de tecnologías como React Native o Ionic. O incluso Flutter aunque se use Dart.</p>
<p><strong>¿Qué opinas sobre la formación oficial a día de hoy merece la pena? (carrera, máster..)</strong></p>
<p>Si lo enfocamos exclusivamente en aprender cosas útiles quizá la universidad no sea la mejor opción.</p>
<p>Pero si ponemos en valor la diversión y las relaciones personales que construyes en esa etapa empieza a ser interesante.</p>
<p>La universidad se puede enfocar de muchas formas. Puedes aprovechar al máximo tu estancia, juntarte con estudiantes con las mismas inquietudes que tú, lanzar muchos side projects, utilizar recursos de la universidad… o puedes dedicarte a intentar aprobar los examenes.</p>
<p>Yo fui de los segundos y por eso mi estancia universitaria no fue la mejor. Al menos hice amigos.</p>
<p><strong>¿Hiciste un máster relacionado con gestión de empresas verdad? ¿Lo volverías a hacer?</strong></p>
<p>Sí. Me cambió la vida. Y no el master en sí, si no la oportunidad de entrar en Marina de Empresas.</p>
<p>Marina de Empresas es un polo emprendedor en Valencia impresionante. Juntan una universidad de empresarios (EDEM, donde hice el máster), una aceleradora (Lanzadera) y un fondo de inversión (Angels Capital). Seguramente sea el mejor sitio de España en el que estar si quieres emprender.</p>
<p>Ahí pude conocer a otros estudiantes con ganas de emprender, mi proyecto (Flamingueo) pudo ser acelerado por Lanzadera y finalmente Angels Capital invirtió.</p>
<p>Sin ellos Flamingueo no existiría y yo no habría conocido a decenas de emprendedores increíbles.</p>
<h2>Organización</h2>
<p><strong>¿Qué servicios web (ej asana) recomiendas en tu dia a dia?</strong></p>
<p>Esto depende mucho del tamaño del proyecto. Como norma general soy de los que piensa que cuanto menos mejor. A veces dedicamos más tiempo a las herramientas que al plan.</p>
<p>Utilizo Notion para ponerme Objetivos, ideas y tareas. También es útil como repositorio de procesos cuando la empresa es algo más grande.</p>
<p><strong>¿Qué apps recomiendas en tu día a día?</strong></p>
<p>Intento utilizar las mínimas posibles porque me generan adicción. No utilizo ninguna que no sea típica (gmail, google calendar…) e intento usarlas poco.</p>
<p><strong>¿Usas alguna técnica para concentrarte (por ejemplo pomodoro)?</strong></p>
<p>Uso el método de Ivy Lee. Suena muy pro pero es lo más básico posible (por eso me funciona).</p>
<p>Necesitas una libreta y un boli. Importante NO usar apps.</p>
<p>Cada día pones en una hoja la fecha y pones entre 1 y 6 tareas importantes que realizar. Con importantes no me refiero a “contestar email”. Tampoco son tareas urgentes. Sólo aquellas tareas que benefician a tu yo futuro.</p>
<p><strong>¿Te gusta más programar de día, tarde o noche?</strong></p>
<p>Me gusta programar a las 6 am. De 6 am a 10 am mi concentración es mucho más alta. Imagino que esto me ocurre porque recibo menos interrupciones y tentaciones a esas horas.</p>
<p>Ahora, no consigo levantarme a las 6 muchas veces jaja.</p>
<p><strong>¿Sueles tener muchas ideas o funcionalidades que quieres añadir? ¿Cómo te gestionas para no estar disperso, querer hacerlo todo y ser más productivo?</strong></p>
<p>Suelo tener muchas ideas y trato de decir NO la mayor parte del tiempo. La clave para mi es tener muy clara la visión y la misión de la empresa/sideproject. Esto te ayuda a decir no a muchas cosas y te permite priorizar y hacer solo lo importante.</p>
<p>Al principio la mejor forma de aportar valor es hacer muy pocas cosas bien hechas dirigidas a un público muy concreto. Y a pesar de saberlo, no puedo evitar dispersarme en muchas ocasiones.</p>
<p>[/lock]</p>]]></content:encoded>
</item><item>
<title><![CDATA[Entrevista a Álvaro Trigo - Indie developer]]></title>
<link>https://betabeers.com/blog/entrevista-a-alvaro-trigo-indie-developer-408/</link>
<pubDate>Mon, 21 Dec 2020 17:28:09 +0000</pubDate>
<category>Entrevistas</category>
<content:encoded><![CDATA[<p><img src="https://betabeers.com/uploads/blog/20201221_alvaro_trigo.png" alt="Entrevista a Álvaro Trigo - Indie developer" class="img_post" target="_blank" rel="nofollow" /></p>
<p>Hace un tiempo que sigo en twitter a <a href="https://twitter.com/IMAC2" target="_blank" rel="nofollow">Álvaro Trigo</a> y me parece una persona muy intersante por lo que hace, monetiza sus sideprojects (<a href="https://alvarotrigo.com/fullPage/es/" target="_blank" rel="nofollow">fullpage.js</a> y <a href="https://fullstats.io/" target="_blank" rel="nofollow">fullstats</a>), además comparte muchos trucos de programación en twitter que quizás no sabías.</p>
<p>[toc]</p>
<h2>Personal</h2>
<p><strong>¿Recuerdas a qué edad y cómo empezaste a programar? ¿Te motivó alguna persona, película….?</strong></p>
<p>Recuerdo que creé mi primera página web con 15 años, cuando el profesor de informática nos enseñó a hacer una página web en Microsoft Word. Creé mi propia página y comencé a hacer cosillas. </p>
<p>También empecé a cacharrear un poco con el Counter Strike y a crear mis propios mapas, cambiar voces y demás. Pero eso no requería programación. </p>
<p>Poco después pasé del Word al Dreamweaver donde ya se podía ver el código HTML y seguramente fue cuando empecé a tocar cosillas y hacer eso del copy/paste. </p>
<p>Lo que si recuerdo fue mi primer código PHP. Quería conseguir los email de los visitantes de una de mis página web y creé un formulario para ello. Imprimí un par de tutoriales que vi por internet y me los llevé al pueblo un día de primavera. Tendría 16 o 17 años. </p>
<p>Días después conseguí hacer el POST y enviarlos a la base de datos. </p>
<p>La motivación era la idea de hacer una página web y hacerla pública. Hacer algo útil para otros. Parece poca cosa supongo, pero en aquella época donde hacer páginas web no era ni tan sencillo ni tan común como hoy en día, me hacía mucha ilusión. </p>
<p><strong>¿Alguién de tu círculo cercano como pueden ser familiares o amigos saben que es lo que haces?</strong></p>
<p>Jaja! Buena pregunta! 
Supongo que esto nos pasa a casi todos los que nos dedicamos a cualquiera rama de la informática. </p>
<p>Mis familiares y amigos más cercanos saben que tengo un “plugin” y que también tengo algunos proyectos en paralelo, pero si es verdad que muchos no entienden cómo me puedo dedicar a ello a tiempo completo o como puedo vivir de ello.</p>
<p><strong>¿Qué te llevó a UK?</strong></p>
<p>El intentar ir a EEUU!</p>
<p>Cuando estaba estudiando la carrera de informática en Burgos, en 5º, me apunté a una “beca no Erasmus” para cursar 4 meses en una universidad de Estados Unidos y me la concedieron junto a 3 otros estudiantes. La experiencia me encantó! </p>
<p>No es el mismo rollo que una beca Erasmus en cuanto a que por ejemplo solo éramos 15 estudiantes internacionales con este tipo de beca en la universidad de destino y de ellos solo 4 eramos Españoles. Pero vivir en un campus norteamericano y en un país diferente durante un tiempo es algo que no había hecho antes y me pareció una gran experiencia. </p>
<p>Al terminar la carrera tenía ganas de más e intenté encontrar trabajo en EEUU. Lamentablemente no es tan fácil por tema de visados y demás y entonces opté por buscar trabajo en Inglaterra. Y la verdad es que genial también! Cambridge es bastante especial!</p>
<p><strong>¿Qué es lo que te gusta de UK como programador (ej eventos) y persona (ej comida)?</strong></p>
<p>Yo creo que lo que más me gusta de Inglaterra es que se trata muy bien a los desarrolladores y se les valora muchísimo. Pondría eso por encima del resto de aspectos.
El trato en el trabajo, el salario, las horas de trabajo etc. </p>
<p>En cuanto a eventos yo solamente fui a uno de los grandes. A la Smashingconf en Oxford que duró un par de días. Supongo que una de las ventajas de que sea en inglés es que atrae a muchos ponentes de EEUU o países de habla inglesa y puedes tener como ponente a gente con cargos importantes en las grandes tecnológicas. </p>
<p>La comida no me ocasionó ningún problema del otro mundo. Lo único el tomate frito! Ese es más difícil de conseguir! 
Me parece que es bastante más barato comer a medio dia que España y que es mucho más sencillo encontrar sitios de comida para comer fuera todos los días, que es básicamente lo que yo hacía.</p>
<p><strong>Veo que en twitter sueles compartir trucos de programación.. ¿Puedes contar para quien no te siga las cosas que te gusta publicar?</strong></p>
<p><a href="https://twitter.com/IMAC2/status/1327227452461096960" target="_blank" rel="nofollow"><img src="https://betabeers.com/uploads/blog/20201221_EmtBjMyXUAIVdk0.png" alt="Trucos de programación" class="img_post" target="_blank" rel="nofollow" /></a></p>
<p>Si un día llueve o nieva mucho, no será a través de mi como te enteres :)</p>
<p>Me dedico a publicar casi exclusivamente contenido relacionado con el desarrollo web. Desde pequeños consejos de programación hasta pequeños ejercicios para auto testearte y cosas que encuentro curiosas o que voy descubriendo. </p>
<p>También tweeteo cosas en las que estoy trabajando, opiniones sobre varias, frases motivaciones y demás. </p>
<p><strong>¿Cual es la historia de tu nickname IMAC2?</strong></p>
<p>El primer ordenador en mi casa fue un Macintosh Classic II. Tengo que decir algo más? :)
Vamos, que era un “maquero/fanboy” en aquella época.</p>
<p>Supongo que “imac” estaba pillado y opté por poner un 2 :) 
He pensado en cambiarlo pero aún no he dado el paso. </p>
<p><strong>¿Si no fueras programador a que crees que te gustaría dedicarte?</strong></p>
<p>Me gusta bastante el tema del marketing, de modo que podría ser un candidato también. Es algo que tengo que hacer si o sí a día de hoy y que ya hacía en su día con mis primeras páginas web. </p>
<p>Fotógrafo o diseñador tampoco estarían nada mal. O ya que estamos, videógrafo también. Me flipa un video bien estabilizado y bien montado y me gusta hacer mis pequeñas cosillas con videos de mis viajes o incluso videos promocionales para mis productos. </p>
<h2>Sideprojects</h2>
<p><strong>¿Qué opinas de los sideprojects? Muchos pensarán que son una pérdida de tiempo pero tu has conseguido ganarte la vida con ellos</strong></p>
<p>Yo creo que depende de cada uno, de lo que le guste hacer en su tiempo libre o de las prioridades que tenga en cada momento.</p>
<p>No creo que sea necesario tener side projects para ser bueno en tu trabajo, pero si tener un side project es algo que te gusta y que crees que además te puede beneficiar a nivel personal o profesional, pues bienvenido sea. Y sino, pues también. Nadie tiene que sentirse mal por no tener un side project. </p>
<p>Yo comencé fullPage.js porque tenía ganas de aprender a hacer un plugin de jQuery y creí que sería una buena idea para un plugin. Mientras lo hacía también me di cuenta de que estaba aprendiendo bastante, y eso unido a la motivación de que hay gente usando algo que tu has creado, es lo que me impulsó a seguir trabajando en él. </p>
<p><strong>¿Hasta poder vivir de tus sideprojects como te financiaste?</strong></p>
<p>Trabajaba en Cambridge a tiempo completo como desarrollador web “full-stack” en una empresa. Nunca tuve que arriesgar nada la verdad. El tiempo era mi única inversión. </p>
<p>Solamente decidí dejar el trabajo cuando ya mis beneficios a través de mi proyecto igualaron a las de mi salario.</p>
<p><strong>¿Para quien no conozca fullpage podrías contar un poco de que se trata? Tu cliente imagino que deben ser programadores y agencias de marketing, viendo que hay muchas librerias similares ¿En qué crees que destaca tu producto?</strong></p>
<p>fullPage.js es una libraría de JavaScript que básicamente permite hacer un “slider a pantalla completa”, tanto vertical como horizontal y que permite moverte de una sección a otra a 
través de la ruleta del ratón o el trackpad. Tiene decenas de opciones que resultan muy útiles a los desarrolladores, callbacks, modo responsive, classes de estados, anclas de URL etc.</p>
<p>En el momento en el que creé el plugin no existía nada similar. Me di cuenta de ello cuando tuve que realizar una página web con este efecto y tuve que hacerlo desde cero. Eso fue principalmente lo que generó ese impulso inicial. </p>
<p>A día de hoy existen algunos proyectos similares pero fullPage.js se diferencia en varias cosas:</p>
<ul>
<li>Compatible con navegadores antiguos hasta IE 9.</li>
<li>Muchas más flexibilidad en cuanto a las cientos de combinaciones que ofrece.</li>
<li>Ofrece extensiones de pago con efectos más allá del mero slider.</li>
<li>Desplazamiento vertical y horizontal.</li>
<li>Tiene wrappers para los grandes frameworks (Vue, Angular, React…)</li>
<li>Tiene plugin para Wordpress (Elementor, Gutenberg, Divi…)</li>
<li>Y varias cosillas más pero que tampoco voy a nombrar en profundidad aquí. </li>
</ul>
<p>Algo que también creo que es bastante importante es el hecho de que es un software que lleva años en el mercado y que ha sido usado y testeado por miles de personas / empresas, lo que también da algo más de confianza en el proyecto.</p>
<p>Eso, y que tiene una persona, yo, que estoy detrás del proyecto día a día y que si hace falta puedo trabajar a tiempo completo en él, resolviendo bugs, ofreciendo soporte o añadiendo mejoras poco a poco. Cosa que muchos otros no pueden permitirse. </p>
<p><strong>Veo que fullpage lo han usado grandes empresas ¿Cómo lo diste a conocer al prinicpio? ¿Y qué haces ahora para seguir promocionándolo?</strong></p>
<p>El lanzamiento del plugin coincidió con las fechas en las que Apple lanzó el iPhone 5C y dio la casualidad de que la página del iPhone usaba un efecto muy similar al que mi plugin ofrecía. Supongo que eso ayudó un poco al respecto. </p>
<p>Por mi parte publiqué el proyecto en Github, escribí un correo a unos 10 blogs de desarrollo web y lo añadí en un par de páginas de librerías y plugins JavaScript.</p>
<p>Al cabo de unos días recuerdo haberme metido en Github y me quedé sorprendido al ver que ya tenía más de 500 stars y la gente empezaba a enviarme correos pidiendo ciertas funcionalidades y reportando errores. </p>
<p>Respecto a las grandes empresas usando fullpage.js, todas están compuestas de desarrolladores como tu y como yo, de igual que tu decides usarlo para tu página web ellos pueden decidir usarlo para su próximo producto o página web. La cuestión es convencer a los desarrolladores, no a las empresas :) </p>
<p><strong>¿Has vendido en envato u otros marketplaces que opinas al respecto?</strong></p>
<p>Nunca he probado marketplaces. De hecho fullPage.js fue totalmente gratis durante 3 años. Nunca pensé en monetizarlo la verdad, es algo que surgió de manera natural.</p>
<p>Durante esos 3 años varias personas me pedían que hiciera algunas modificaciones y me contrataban para ello. Algunos pedían exáctamente la misma funcionalidad y pensé: “Y si creo extensiones para este tipo de cambios y las cuelgo en la página a un precio mucho más bajo? De este modo no tendría que hacer nada, me ahorraría tiempo y tal vez le interesen a más gente.”</p>
<p>Y eso hice. La gente encontraba la librería a través de buscadores, foros, blogs y demás y de paso las extensiones que yo vendía. Fue simplemente el camino más natural para mi. Los marketplace se quedan una comisión bastante grande y fuerzan los precios a la baja. Yo como ya tenía una audiencia no necesité tirar de marketplaces para atraer compradores. </p>
<p><strong>¿Puedes contar como vendes código, si usas una plataforma, como lo haces para protegerlo y como avisar a los clientes para que tengan acceso al codigo actualizado?</strong></p>
<p>Uso gumroad.com como plataforma de venta. Simplifica mucho todo el tema del IVA Europeo (lo declaran ellos) y es la razón principal por la que lo uso. Tiene algunas carencias pero definitivamente creo que es el modo más sencillo y rápido de poner algo a la venta en internet. </p>
<p>El proceso es sencillo.</p>
<ol>
<li>Pongo un link a la página del producto en Gumroad.</li>
<li>Los usuarios reciben una clave de licencia. (aportada por Gumroad)</li>
<li>Reciben notificaciones por correo cuando hago actualizaciones. </li>
<li>Las extensiones requieren de una clave que se genera usando la clave de licencia.</li>
</ol>
<p><strong>¿Qué pasos le recomiendarias a una persona que sepa programar pero todavía no se ha centrado en un sideproject? ¿Qué te hizo decantarte por fullpage y no otros proyectos?</strong></p>
<p>Para mi fue el camino natural. No tuve que pararme a pensar. Simplemente surgió y salió bien. Si no hubiese sido fullPage.js tal vez hubiese sido otro proyecto. Nunca lo sabré.</p>
<p>¿Qué recomendaría? Pensar en algo que te gustaría implementar como solución a algún problema. Tienes que estar motivado para empezar porque si no no tiene sentido. Y tienes que estar lo suficientemente convencido para invertir tiempo en ello y no dejarlo a medias.</p>
<p>Empieza pensando en pequeño. Resuelve problemas sencillos que no te vayan a llevar meses. Ve si hay interés o no y sino pasa a otro proyecto o pivota en base al feedback. </p>
<p>Lo típico. Fail fast, fail often.</p>
<p>Cuanto más pequeño y factible veas un proyecto, más te motivará trabajar en él porque lo verás como algo posible. Y por supuesto, si no triunfa no te llevarás el golpe de haber perdido meses o años de tu vida en ello.</p>
<p><strong>Recientemente has lanzado fullstats ¿Nos puedes contar de que trata?</strong></p>
<p><a href="https://fullstats.io/" target="_blank" rel="nofollow"><img src="https://betabeers.com/uploads/blog/20201221_fullstats.png" alt="fullstats" class="img_post" target="_blank" rel="nofollow" /></a></p>
<p>Si claro. Es una plataforma que permite obtener estadísticas avanzadas para las ventas realizadas a través de la plataforma de venta llamada Gumroad.  Para los que conozcáis Baremetrics, es un poco el Baremetrics para Gumroad.</p>
<p>Gumroad provee un panel de estadísticas pero se queda bastante corto. Es muy básico y no se pueden obtener ciertas conclusiones a través de ellos. </p>
<p>Comencé a pensar en él hace años cuando intentaba tomar decisiones basadas en datos para maximizar mis beneficios. Empecé creándolo para mi mismo pero pronto vi que había más gente interesada en algo así. De modo que pensé en ofrecerlo para otros también. </p>
<p><strong>¿No te preocupa que gumroad copie todas las funcionalidades de fullstats?</strong></p>
<p>No mucho la verdad. Si lo copia al menos seguiré teniendo la información que necesito. Y sino, pues seguiré desarrollando el mío en base a mis necesidades o las de los clientes que vaya teniendo.</p>
<p>No obstante, Gumroad ofrece su roadmap de manera pública y no parece que tengan mucha intención de invertir muchos recursos en un sistema de estadísticas más avanzado. Además tampoco parece que sea algo que les vaya a beneficiar mucho a corto plazo.</p>
<p>Otra opción que no descarto y que ojalá suceda, es que Gumroad compre mi plataforma. Es una empresa muy dinámica, pequeña y bastante abierta y de hecho el CEO Sahil conoce la existencia de fullstats y alguno de los empleados es cliente de la misma.</p>
<p><strong>¿Cual es el stack de fullstats?</strong></p>
<p>Bastante sencillito. </p>
<ul>
<li>PHP con mi propio framework (si, php!)</li>
<li>CSS a pelo</li>
<li>JavaScript / jQuery y un pelín de Vue</li>
<li>MariaDB y SQL a pelo</li>
</ul>
<p>Soy un poco del clan de @levelsio y creo que lo importante no es tanto el stack sino como poder realizar un prototipo rápido y ver si hay interés. </p>
<p>Siempre se pueden ir cambiando cosillas en un futuro si hiciese falta. 
Y si os preguntáis porque no usé frameworks tipo Vue o React es simplemente porque no los usé nunca (o bastante poco) y tiré por lo que conocía. </p>
<p>[lock]</p>
<h2>Tecnologías</h2>
<p><strong>¿Cual es tu stack favorito?</strong></p>
<p>Un poco a la antigua usanza:</p>
<ul>
<li>PHP </li>
<li>MariaDB</li>
<li>JS a pelo o con Knockout.js</li>
<li>Gulp </li>
<li>Github</li>
</ul>
<p>No soy muy fan de los frameworks de PHP y prefiero usar el mío propio. Para lo que voy haciendo por ahora creo me basta y así me quito de pensar en actualizar versiones, dependencias, errores imposibles de trackear y demás framework-related-movidas. </p>
<p>He usado node un poco con Express.js en algún proyecto propio para alguna cosilla y anteriormente en el trabajo, pero no siento que lo necesite por ahora en muchos proyectos. </p>
<p>Hasta hace poco subía archivos usando FileZilla tipo @levelsio pero para nuevos proyectos tiendo a tirar de una tarea de Gulp + rsync. </p>
<p>Decir que me gustaría echar un ojo un poco más a Vue pero es algo que aún tengo pendiente. Por ahora tiro de Knockout.js en algunos proyectos, que aunque es viejo hace su función. </p>
<p><strong>¿Alguna tecnología o implementación de alguna o varias existentes que te haya sorprendido?</strong></p>
<p>La verdad es que me flipa el tema del canvas, los shaders y demás animaciones. Es un mundillo en el que me estoy metiendo un poco ahora para lanzar una nueva extensión pero en el que me siento como un extraterrestre recién llegado a la tierra. 
Me gustaría también mirar un poco más a fondo el framework de Tailwind para CSS que tiene bastante buena pinta.</p>
<p><strong>No se suele hablar mucho de errores o cagadas, ¿Recuerdas alguna situación de tierra trágame con la progamación? Yo por ejemplo me cargué la base de datos en mi primer día en una startup</strong></p>
<p>Tampoco recuerdo muchas ahora mismo. La última casi me cargo todos los commit que hice durante 3 o 4 meses pero para los que no hice el push. </p>
<p>Tuve un fallo al intentar hacer un push de un archivo de más de 100Mb en Github y la cosa se quedó entre medias, que ni me dejaba hacer push ni el revert del commit ni nada. Como yo soy un poco torpe con esto del Git busqué una solución rápida por internet y di con alguien que recomendaba hacer un “git reset --hard e78e9f6”.</p>
<p>Vamos, que después de eso vi en tiempo real como iban desapareciendo archivos de mi editor de texto. Ya podéis imaginar la cara de tonto que se me quedó. </p>
<p>Básicamente había dicho a Git, olvidate de todos los cambios desde que hice el commit  con id: e78e9f6. Esto pasa cuando una va con prisas y ni se molesta en mirar de que va ese comando, bastante descriptivo por cierto, llamado “reset”.</p>
<p>Asi que después estuve un par de horas intentando ver cómo podía recuperar todos los archivos y cambios perdidos. Al final logré recuperar casi todos, pero alguno se me resistió. </p>
<p><strong>¿Qué webs o canales recomiendas para ponerse al día con la programación?</strong></p>
<p>Twitter sería el que pondría como medio principal. Parece que no pero uno está bastante al día de lo que se cuece por ahí cuando sigues a las personas indicadas dentro del mundillo por donde te mueves.</p>
<p>A parte de eso, la página de <a href="https://www.smashingmagazine.com" rel="nofollow" target="_blank">https://www.smashingmagazine.com</a> siempre tiene artículos muy en detalle sobre temas muy variopintos dentro del desarrollo web. </p>
<p>Tengo que decir que yo tampoco soy una persona a la que la guste cacharrear muchísimo con todo lo que va apareciendo por ahí. Tiendo un poco más a ver la programación como el medio y no como el fin, de modo que habrá muchas personas que puedan responder esta pregunta mucho mejor que yo. </p>
<h2>Eduación</h2>
<p><strong>¿Cual sería el primer lenguaje de programación que le recomendarías a alguien que quiere aprender a programar?</strong></p>
<p>JavaScript. Porque no se necesita nada más que un navegador, un archivo html o un simple codepen.<br />
Ni servidores, ni bases de datos, ni instalaciones ni nada.</p>
<p>A parte de eso yo creo que a la gente le motivará mucho más el ver que puede hacer algo interactivo en cuestión de minutos. </p>
<p><strong>¿Qué opinas sobre la formación oficial a día de hoy merece la pena? (carrera, máster..)</strong></p>
<p>Yo hice la técnica y la superior de informática. Personalmente creo que sobran muchas asignaturas, muchas horas de relleno y mucho de aprender de memoria, pero si es verdad que te ayuda un poco a aprender lo básico y a abrirte la mente a nuevas cosas que tal vez de otro modo nunca hubieses sabido que estaban allí. </p>
<p>¿Merece la pena? Depende del objetivo.
Si tu objetivo es encontrar un trabajo habitual en España, seguramente sí. Aquí lamentablemente sigue habiendo bastante titulitis. </p>
<p>Si tu objetivo es crear una startup o empezar tu propio SAAS, tal vez no. Tal vez aprendas mucho más por tu cuenta. La cuestión es, ¿Te pondrías a estudiar estas cosas por tu cuenta? </p>
<p>Una cosa si que tengo clara. No hace falta tener una carrera para ser un buen desarrollador o para crear una startup. ¿Pero puede abrirte puertas tener una carrera? También. </p>
<h2>Organización</h2>
<p><strong>¿Qué servicios web (ej asana) recomiendas en tu día a día?</strong></p>
<p>Tanto para mi mismo como para administrar tareas entre mi pequeño equipo (somos 4 personas ahora y anteriormente 5), uso:</p>
<p>Github como repositorio y para tareas también.</p>
<ul>
<li>Todoist (más a nivel personal, típica lista de cosas)</li>
<li>Freshdesk (tickets de soporte) </li>
<li>Notion (notas y documentos varios)</li>
<li>Slack </li>
</ul>
<p><strong> ¿Qué apps recomiendas en tu dia a dia?</strong></p>
<ul>
<li>Todoist </li>
<li>Gmail</li>
</ul>
<p><strong>¿Usas alguna técnica para concentrarte (por ejemplo pomodoro)?</strong></p>
<p>Simplemente me pongo un horario de trabajo e intento hacer mis horas. Entre 6 y 7 al día.
No tengo mucho problema de concentración porque lo que hago tienda a gustarme y a motivarme. Es más, muchas veces tengo que forzarme a terminar antes.</p>
<p>También me gusta hacer ejercicio 3 veces por semana porque me ayuda a sentirme mejor. </p>
<p><strong>¿Te gusta más programar de día, tarde o noche?</strong></p>
<p>No me importa mucho. Mi horario de trabajo lo tengo planificado hasta las 6 o 7 de la tarde pero si surge la inspiración me puedes encontrar un Viernes a las 3 de la mañana tecleando con ojos de drogadicto.</p>
<p><strong>¿Sueles tener muchas ideas o funcionalidades que quieres añadir? ¿Cómo te gestionas para no estar disperso, querer hacerlo todo y ser más productivo?</strong></p>
<p>Esto si que es un problema.</p>
<p>Cuanto más trabaja uno en diferentes cosas más ideas parecen surgir! </p>
<p>Tiendo a apuntar mis ideas en Github o en Todoist para no olvidarme de ellas. Otras, si las veo fáciles de implementar puedo tirarme directamente a ellas, pero es verdad que si uno quiere avanzar y enfocarse en algo tiene que tener un pequeño roadmap.</p>
<p>Tiendo a ponerme pequeñas metas, tanto a largo como a más corto plazo. Tipo… quiero lanzar esto antes de fin de año, o quiero lanzar una versión nueva esta semana. Luego intentar priorizar las que me ayuden a llegar a dichas metas.</p>
<p>Pero es verdad que siempre surgen nuevas por el camino y muchas veces es difícil decir que no y uno puede acabar despistándose un poco... Pero bueno, supongo que tampoco es el fin del mundo cuando uno trabaja para si mismo :) </p>
<p>Creo que lo que más ayuda a enfocarse es tener usuarios o clientes pidiendo cosas o esperando a que las lances. Entonces hay una presión que te ayuda a enfocarte mucho más. </p>
<p>[/lock]</p>]]></content:encoded>
</item><item>
<title><![CDATA[Consultando información remota de WordPress usando API REST]]></title>
<link>https://betabeers.com/blog/consultando-informacion-remota-wordpress-usando-api-rest-407/</link>
<pubDate>Fri, 31 May 2019 07:47:26 +0000</pubDate>
<category>Colaboraciones</category>
<content:encoded><![CDATA[<p>La WordPress REST API no es una funcionalidad especialmente reciente de WordPress, puesto que ya desde hace algunos a&ntilde;os hemos podido tener acceso y disfrutar de sus ventajas en nuestros desarrollo. En cualquier caso, nunca es tarde para recordar su disponibilidad en nuestras instalaciones de WordPress y poner en pr&aacute;ctica su aplicaci&oacute;n con algunos ejemplos de uso.</p>
<p>En las versiones m&aacute;s recientes, es un elemento que adem&aacute;s est&aacute; incorporado en el core de WordPress y que por defecto se encuentra activa, hecho que nos permitir&aacute; empezar a consumir datos sin necesidad de realizar configuraciones previas.</p>
<p><img src="http://betabeers.com/uploads/blog/20190531_informacion-remota-rest-api-betabeers-blog-imagen.jpg" alt="" width="600" height="400" /></p>
<p>La REST API de WordPress permite a cualquier desarrollador separar la capa de frontend de un desarrollo, de la capa de administraci&oacute;n de datos de WordPress ampliando los l&iacute;mites al poder utilizar esa informaci&oacute;n a trav&eacute;s de otras tecnolog&iacute;as y lenguajes de programaci&oacute;n.</p>
<h2>Conociendo la REST API de WordPress</h2>
<p>Tenemos diferentes elementos a tener en cuenta en el uso de la API de WordPress que pasamos a detallar:</p>
<h3>Rutas y endpoints</h3>
<p>Los paths nos permiten navegar a trav&eacute;s de diferentes puntos finales o endpoints. Por ejemplo /wp-json/ es una ruta configurada como punto final, y est&aacute; pensada para mostrarnos m&aacute;s rutas disponibles.</p>
<h3>Peticiones</h3>
<p>Las peticiones o requests ofrecen los datos que hemos solicitado en la consulta o devuelven un error si algo no sali&oacute; correctamente.</p>
<h3>Esquemas</h3>
<p>Los esquemas o schemas son los modelos con los que se ofrecen esas respuetas, y nos permiten buscar los datos concretos que queremos consumir de la API.</p>
<h3>Clases de controlador</h3>
<p>Las clases de controlador o controller classes permiten construir nuestras propias rutas y endpoints.</p>
<p>Si queremos tener informaci&oacute;n m&aacute;s espec&iacute;fica sobre cada uno de los elementos de la REST API podemos acudir a la <a href="https://developer.wordpress.org/rest-api/rest-api-handbook-3/">documentaci&oacute;n oficial</a>.</p>
<p>Llamadas HTTP a la REST API</p>
<p>Para poder acceder a los puntos finales m&aacute;s &uacute;tiles deberemos realizar una llamada HTTP en primer lugar a la API REST. El fundamento b&aacute;sico de cualquier llamada es el siguiente,   donde deberemos reemplazar el indicador por nuestro propio dominio.</p>
<p>http://dominio.com/wp-json/</p>
<p>Si necesitamos comprobar la conectividad podemos ejecutar el comando CURL usando nuestra propia URL desde la terminal:</p>
<p>curl -X OPTIONS -i http://dominio.com/wp-json/</p>
<p>Podemos repetir este comando utilizando varios de los puntos finales principales. En este caso, usaremos GET a tal efecto</p>
<p>curl -X GET -i http://dominio.com/wp-json/wp/v2/posts</p>
<p>Nos muestra un listado de las publicaciones de nuestra instalaci&oacute;n de WordPress.</p>
<p>curl -X GET -i http://dominio.com/wp-json/wp/v2/pages<br /> <br /> Nos muestra un listado de las p&aacute;ginas publicadas de nuestra instalaci&oacute;n de WordPress.</p>
<p>Si estamos buscando una gu&iacute;a de referencia de los endpoints de la REST API podemos tambi&eacute;n consultar esta <a href="https://developer.wordpress.org/rest-api/reference/">gu&iacute;a espec&iacute;fica de referencias</a>.</p>
<h3>Consultando informaci&oacute;n remota de WordPress usando API REST</h3>
<p>Si estamos pensando en poner en pr&aacute;ctica el uso de la REST API para nuestros desarrollos hay muchos ejemplos que podemos tener en cuenta, pero en este caso buscaremos hacerlo con un caso pr&aacute;ctico. Supongamos que queremos a&ntilde;adir en un desarrollo propio informaci&oacute;n p&uacute;blica de otro sitio externo.</p>
<p>Estamos hablando en cualquier caso de otro sitio web creado con WordPress y al cual no tenemos acceso al no ser administradores o propietarios de ese sitio, por ejemplo un portal de noticias.</p>
<p>Es posible que queramos incluir una secci&oacute;n interna en nuestra web que nos permita obtener las &uacute;ltimas noticias de ese portal para poder enlazarlas y completar con informaci&oacute;n de valor esa secci&oacute;n en concreto.</p>
<p>Habitualmente los feeds RSS ven&iacute;an siendo una soluci&oacute;n para obtener esa informaci&oacute;n, pero la forma en la que podemos manipular esa informaci&oacute;n y como se muestra es muy limitada. Este es un caso concreto donde podemos utilizar la REST API para ello.</p>
<p>En este caso crearemos un sencillo script en PHP que utilizar&aacute; la REST API de un tercero para poder consultar la informaci&oacute;n y utilizarla en un desarrollo propio.</p>
<p>En primer lugar deberemos acceder a la consulta HTTP del endpoint que necesitamos conocer:</p>
<p>http://www.dominio.com/wp-json/wp/v2/posts/?page=1&amp;per_page=3</p>
<p>En este caso estamos consultando la &uacute;ltima versi&oacute;n de la REST API y a&ntilde;adi&eacute;ndole algunos par&aacute;metros para limitar esa consulta a determinados datos. En este caso, tres resultados por p&aacute;gina son los que nos van a interesar.</p>
<p>Con &oacute;rdenes de PHP vamos a decodificar los datos JSON para que podamos utilizarlos como creamos conveniente. Recordemos que ese dominio es externo, es decir, podr&iacute;amos realizar esta petici&oacute;n sobre cualquier instalaci&oacute;n de WordPress reciente con la API REST activa.</p>
<p>$json = file_get_contents($uri);</p>
<p>$posts= json_decode($json);</p>
<p>Recorremos la informaci&oacute;n facilitada y nos quedamos con algunos par&aacute;metros que nos puedan interesar para imprimir el HTML final.</p>
<p>foreach ($posts as $post)</p>
<p>Y finalmente hacemos la llamada a la informaci&oacute;n que nos interesa, como por ejemplo el permalink de cada entrada, el t&iacute;tulo o el extracto. Informaci&oacute;n toda ella disponible desde la API REST.</p>
<p>   echo "&lt;h2&gt;&lt;a href='" . $post-&gt;link . "'&gt;" . $post-&gt;title-&gt;rendered . "&lt;/a&gt;&lt;/h2&gt;\n";</p>
<p>   echo "&lt;p&gt;" . $post-&gt;excerpt-&gt;rendered . "&lt;/p&gt;";</p>
<p>El c&oacute;digo completo que podemos utilizar es el siguiente, y tan solo deberemos personalizarlo con la forma que necesitemos para mostrar la informaci&oacute;n.</p>
<p>&lt;?php</p>
<p>$uri = 'http://www.dominio.com/wp-json/wp/v2/posts/?page=1&amp;per_page=3';</p>
<p>$json = file_get_contents($uri);</p>
<p>$posts= json_decode($json);</p>
<p>echo "&lt;h1&gt;Listado de entradas&lt;/h1&gt;&lt;ul&gt;";</p>
<p>foreach ($posts as $post) {</p>
<p>   echo "&lt;h2&gt;&lt;a href='" . $post-&gt;link . "'&gt;" . $post-&gt;title-&gt;rendered . "&lt;/a&gt;&lt;/h2&gt;\n";</p>
<p>   echo "&lt;p&gt;" . $post-&gt;excerpt-&gt;rendered . "&lt;/p&gt;";</p>
<p>   echo "&lt;p&gt;&lt;a href='" . $post-&gt;link . "'&gt;Enlace directo&lt;/a&gt;&lt;/p&gt;\n";</p>
<p>}</p>
<p>echo "&lt;/ul&gt;";</p>
<p><a href="https://gist.github.com/fernandiez/031c22b09e1e80e4c397790a53857e2c" target="_blank">Enlace completo en Gist</a></p>
<p>Con este sencillo ejemplo podemos comenzar a crear nuestras propias consultas de datos sobre otras instalaciones de WordPress, al tratarse de informaci&oacute;n accesible de manera remota, vitaminando de este modo nuestros propios desarrollos y controlando c&oacute;mo se muestra la informaci&oacute;n final.</p>
<h2>Conclusiones</h2>
<p>Es m&aacute;s que interesante conocer las novedades que se van implementando en las nuevas versiones de WordPress, trabajemos o no con el CSM de manera habitual puesto que es posible que en determn</p>]]></content:encoded>
</item><item>
<title><![CDATA[QUIC: lo que se esconde detrás de HTTP/3]]></title>
<link>https://betabeers.com/blog/quic-lo-que-se-esconde-detras-http-3-406/</link>
<pubDate>Fri, 24 May 2019 11:18:01 +0000</pubDate>
<category>Colaboraciones</category>
<content:encoded><![CDATA[<div class="wp-block-image">
<div class="wp-block-image" style="text-align: center;"><img class="wp-image-8494" src="https://solidgeargroup.com/wp-content/uploads/2019/05/quicSmall.png" alt="" /><br />
<div class="wp-block-image" style="text-align: left;"><em>Este art&iacute;culo ha sido escrito por <a href="https://twitter.com/fruizhernandez?lang=es">Fernando Ruiz </a>y publicado originalmente en el blog de <a href="https://solidgeargroup.com/quic-lo-que-se-esconde-detras-de-http-3?lang=es" target="_blank">Solid GEAR</a>.</em><br>
<div class="wp-block-image" style="text-align: left;"><br />
<p><strong>HTTP/3</strong> es la tercera versi&oacute;n oficial del popular protocolo de transferencia de hipertexto utilizado para intercambiar informaci&oacute;n binaria en la World Wide Web. Las mejoras m&aacute;s notables de HTTP/3 vienen de la mano de <strong>QUIC</strong>, un nuevo protocolo de la capa de red desarrollado por <strong>Google</strong>.</p>
<p>El 28 de octubre de 2018, Mark Nottingham, Presidente de los Grupos de Trabajo HTTP y QUIC de la <strong>IETF</strong>, hizo la petici&oacute;n oficial de renombrar HTTP-over-QUIC comoHTTP/3 y en las discusiones posteriores que siguieron y se prolongaron durante varios d&iacute;as, la propuesta de Nottingham fue aceptada por otros miembros de la IETF, quienes en noviembre de 2018 dieron su sello oficial de aprobaci&oacute;n para que HTTP-over-QUIC se convirtiera en HTTP/3.</p>
<h3><strong>Introducci&oacute;n a QUIC</strong></h3>
<p>QUIC (<strong>Quick UDP Internet Connection</strong>) soporta un conjunto de conexiones multiplexadas a trav&eacute;s de UDP y ha sido dise&ntilde;ado para proporcionar una <a href="https://elbauldelprogramador.com/seguridad">seguridad</a> equivalente a TLS/SSL reduciendo la latencia tanto en la conexi&oacute;n como en el transporte de datos.</p>
<p>QUIC viene a resolver algunos de los <strong>problemas detectados en TCP</strong>. Por ejemplo, TCP examina cada paquete en un &uacute;nico flujo de datos y por el contrario, QUIC utiliza el concepto de <strong>streams</strong>, originalmente utilizado en protocolos como HTTP/2 y SCTP. Cuando a cada recurso se le asigna un flujo conceptual individual, es posible que la capa de transporte sepa que, cuando se pierde un paquete, los paquetes siguientes pueden seguir utiliz&aacute;ndose si contienen datos de otro recurso que no estaba en el paquete perdido. Otro ejemplo de problemas que QUIC resuelve es el lento establecimiento de la conexi&oacute;n de TLS/SSL, el cual requiere varios intercambios de informaci&oacute;n (round trip times, <strong>RTTs</strong>), mientras que QUIC reduce este coste de conexi&oacute;n a pr&aacute;cticamente cero RTTs.</p>
<p> </p>
<div class="wp-block-image" style="text-align: center;"><img class="wp-image-8291" src="https://solidgeargroup.com/wp-content/uploads/2019/05/0rtt-graphic.png" alt="" width="650" height="402" /><em>Handshakes en TCP, TCP+TLS y QUIC</em><br />
<h3 style="text-align: left;"><strong>Handshake</strong></h3>
<p style="text-align: left;">La mejora en el handshake de QUIC se basa, entre otros, en un cambio relacionado con el <strong>modo de identificar las conexiones</strong>. TCP, durante el proceso de negociaci&oacute;n, utiliza una tupla de 4 elementos que contienen las IP&rsquo;s y lospuertos utilizados en la conexi&oacute;n. Un problema com&uacute;n para TCP es que la conexi&oacute;n no sobrevive cuando cambia alguna de las IP&rsquo;s o puertos de los endpoints (por ejemplo, cuando el cliente cambia de 4G a WiFi). Cuando esto sucede, los paquetes no pueden ser identificados por el otro endpoint y la conexi&oacute;n se finaliza al alcanzar un timeout. Si la conexi&oacute;n sigue siendo deseada por cualquiera de los dos endpoints, deber&aacute; iniciarse de nuevo el proceso de negociaci&oacute;n, lo que conlleva un aumento de la carga de tr&aacute;fico.</p>
<p style="text-align: left;">QUIC resuelve este problema utilizando un <strong>identificador &uacute;nico e independiente </strong>para cada extremo de la conexi&oacute;n. Este ID de conexi&oacute;n tiene un tama&ntilde;o de entre cuatro y dieciocho bytes, que es creado de manera espec&iacute;fica para la implementaci&oacute;n de cada endpoint, lo que asegura que el endpoint pueda identificar su propio ID en un paquete QUIC, ya que el ID de conexi&oacute;n de destino est&aacute; presente en cada paquete. El hecho de que este ID sea din&aacute;mico permite reducir la sobrecarga de paquetes cuando se utiliza QUIC para configuraciones m&aacute;s simples (por ejemplo, P2P), utilizando un peque&ntilde;o ID de conexi&oacute;n. Sin embargo, en situaciones m&aacute;s complejas (por ejemplo, el balanceo de carga) es posible almacenar informaci&oacute;n adicional en el ID de conexi&oacute;n.</p>
<h4 style="text-align: left;"><strong>Pasos del proceso de negociaci&oacute;n</strong></h4>
<p style="text-align: left;">El handshake de QUIC comienza cuando un cliente env&iacute;a un &ldquo;Hello&rdquo; al servidor, que es un paquete inicial que contiene los identificadores de conexi&oacute;n y la versi&oacute;n de QUIC con la que el cliente desea iniciar dicha conexi&oacute;n. En el payload del paquete se encuentran los datos que son utilizados por TLS para negociar el conjunto de cifrado y otros secretos. Este payload es encriptado usando el algoritmo de encriptaci&oacute;n por defecto de la versi&oacute;n de QUIC utilizada. Todos los paquetes, excepto la negociaci&oacute;n de versiones y el restablecimiento sin estado, se cifran mediante un <strong>algoritmo de cifrado de autenticaci&oacute;ncon datos asociados (AEAD)</strong>. En cuanto a las claves de este algoritmo de encriptaci&oacute;n, se utilizan la versi&oacute;n, el ID de conexi&oacute;n, el n&uacute;mero de paquete y el encabezado en su totalidad para generarlas.</p>
<p style="text-align: left;">A continuaci&oacute;n, cuando el servidor recibe el paquete &ldquo;Hello&rdquo; del cliente, primero comprueba la versi&oacute;n utilizada en la cabecera del paquete. El campo versi&oacute;n es uno de los campos fijos de las cabeceras en QUIC, por lo tanto estar&aacute; presente en las cabeceras de todas las versiones de QUIC. Si la versi&oacute;n dada no est&aacute; en su propia lista de versiones soportadas, el servidor crea un paquete de negociaci&oacute;n de versi&oacute;n y se lo env&iacute;a al cliente. Si la versi&oacute;n es compatible, el servidor responde al paquete con un paquete de negociaci&oacute;n, que se utiliza para enviar datos relacionados con el handshake, que contiene el &ldquo;Hello&rdquo;. Al recibir el &ldquo;Hello&rdquo; del cliente y generar el &ldquo;Hello&rdquo; de vuelta, el servidor ha terminado con la negociaci&oacute;n.</p>
<p style="text-align: left;">Sin embargo, el cliente todav&iacute;a necesita terminar este proceso recibiendo el mensaje &ldquo;Hello&rdquo; del servidor. Cuando el cliente recibe este paquete de negociaci&oacute;n, el handshake se completa en el cliente y puede derivar claves de su <strong>pila TLS</strong> para proteger el payload de todos los paquetes transmitidos. Los paquetes que se encriptan con estas nuevas claves se denominan paquetes <strong>1-RTT protegidos</strong>.</p>
<p style="text-align: left;">El cliente todav&iacute;a necesita indicar al servidor que ha terminado el handshake. Estos datos, procedentes de su pila TLS,se env&iacute;an con un paquete de negociaci&oacute;n. Todos los datos despu&eacute;s de este paquete se env&iacute;an con un paquete protegido 1-RTT. Despu&eacute;s de recibir el &uacute;ltimo paquete de handshake del cliente, el servidor sabe que el cliente ha terminado la negociaci&oacute;n. El servidor tambi&eacute;n puede enviar un <strong>ticket de sesi&oacute;n</strong> al cliente para hacer posible la reanudaci&oacute;n de la sesi&oacute;n en la siguiente conexi&oacute;n que se lleve a cabo.</p>
<p style="text-align: left;"> </p>
<div class="wp-block-image" style="text-align: center;"><img class="wp-image-8328" src="https://solidgeargroup.com/wp-content/uploads/2019/05/1RTT-1.png" alt="" width="600" /><em>Handshake con QUIC con un solo RTT para una conexi&oacute;n segura</em><br />
<h3 style="text-align: left;"><strong>0-RTT</strong></h3>
<p style="text-align: left;">QUIC proporciona una forma de que las conexiones empiecen a intercambiar datos sin esperar a que se produzca un &uacute;nico RTT para el handshake (0-RTT). Esto es posible cuando el cliente inicia una conexi&oacute;n con un servidor con el que ya se hab&iacute;a puesto en contacto anteriormente, por lo que la conexi&oacute;n se inicia con informaci&oacute;n adicional de la sesi&oacute;n de la conexi&oacute;n anterior. Usando esta informaci&oacute;n, es posible exportar claves de encriptaci&oacute;n que pueden ser usadas para encriptar el payload. El servidor que recibe este payload puede exportar las mismas claves de encriptaci&oacute;n de la informaci&oacute;n de sesi&oacute;n que se proporciona en el paquete&rdquo;Hello&rdquo; del cliente. A continuaci&oacute;n, el servidor puede descifrar el payload utilizando las primeras claves de datos.</p>
<div class="wp-block-image" style="text-align: center;"><em>Handshake 0-RTT de QUIC</em><br />
<p style="text-align: left;">Este mecanismo es posible porque QUIC utiliza <strong>TLS/1.3</strong> para el cifrado de datos.</p>
<h3 style="text-align: left;"><strong>Conclusiones</strong></h3>
<p style="text-align: left;">Desde Chrome 29 y Opera 16 ya est&aacute; implementado el soporte para QUIC, lo que significa que estos dos navegadores web ya est&aacute;n preparados para la transici&oacute;n.</p>
<div class="wp-block-image" style="text-align: center;"><img class="wp-image-8298" src="https://solidgeargroup.com/wp-content/uploads/2019/05/QUIC_Chrome.png" alt="" width="500" height="233" /><em>QUIC en Chrome</em><br />
<p style="text-align: left;">Dicho esto, no pretendo decir que QUIC vaya a tener un &eacute;xito desde el principio e inmediatamente gane una gran parte del pastel, sino que mi opini&oacute;n es que la cuota de despliegue ir&aacute; creciendo lentamente, aunque espero que sea m&aacute;s r&aacute;pido que con IPv6</p>
<br /> <br /> <br /> <br /> <br /> <br /> <br>
<br>
<br>
<br>
<br>
<br>
<br>]]></content:encoded>
</item><item>
<title><![CDATA[¿Cómo usar reflection en Go?]]></title>
<link>https://betabeers.com/blog/como-usar-reflection-go-405/</link>
<pubDate>Thu, 16 May 2019 22:29:19 +0000</pubDate>
<category>Colaboraciones</category>
<content:encoded><![CDATA[<p><em>Este art&iacute;culo ha sido escrito por <strong>Adri&aacute;n P&eacute;rez</strong> y publicado originalmente en el blog de <a href="https://blog.friendsofgo.tech/posts/como-usar-reflection-en-golang/%20">Friends of Go</a></em></p>
<p>&iquest;Qu&eacute; es <strong>reflection</strong>? <strong>Reflection</strong> es la habilidad que tiene un programa para examinar y modificar su propia estructura y comportamiento en tiempo de ejecuci&oacute;n. El prop&oacute;sito que tiene <strong>reflection</strong> es la de permitir a los programadores crear c&oacute;digo gen&eacute;rico, adem&aacute;s es la clave para poder realizar <strong>metaprogramaci&oacute;n</strong></p>
<p>Cada lenguaje tiene su propia forma de realizar <strong>reflection</strong> y <strong>Go</strong> no iba a ser menos, pero cada forma de aplicarlo es distinta ya que &eacute;sta se utiliza sobre el sistema de tipos del propio lenguaje.</p>
<p>Dado que <strong>Go</strong> es un lenguaje de tipado est&aacute;tico, es decir, la comprobaci&oacute;n del tipado se realiza durante la compilaci&oacute;n y no durante la ejecuci&oacute;n, permitiendo que los errores de tipado sean detectados antes y que la ejecuci&oacute;n del programa sea mucho m&aacute;s eficiente y segura, y que adem&aacute;s nosotros definimos de que tipo es cada variable durante la programaci&oacute;n, podr&iacute;amos llegar a pensar que <strong>reflection</strong> en <strong>Go</strong> podr&iacute;a no tener sentido.</p>
<p>Pero esa afirmaci&oacute;n no es cierta, ya que podr&iacute;amos querer obtener cierta informaci&oacute;n que no conocemos de antemano, pero no nos quedemos en palabrer&iacute;a vamos a ver <strong>c&oacute;mo se resuelve reflection en GO</strong></p>
<p> </p>
<p><strong><img style="display: block; margin-left: auto; margin-right: auto;" title="golang reflection" src="http://betabeers.com/uploads/blog/20190516_reflection_golang.png" alt="golang reflection" width="814" height="343" /></strong></p>
<h2 id="elpaquetereflect">El paquete reflect</h2>
<p>Como casi siempre que hablamos de una funcionalidad del lenguaje, tenemos un paquete detr&aacute;s que nos facilita la tarea de como enfrentarnos a dicha funcionalidad, en el caso de <strong>reflection</strong> no iba a ser menos y <strong>Go</strong> nos da <a href="https://golang.org/pkg/reflect/">el paquete <strong>reflect</strong></a>.</p>
<p>Antes de entrar en materia queremos destacar, que se asume que sabes lo que haces cuando utilizas dicho paquete, porque muchos de los m&eacute;todos que nos ofrece el paquete si no son bien usados pueden acabar en <code>panic</code>.</p>
<h2 id="laclavedereflectionengo">La clave de reflection en Go</h2>
<p>Para entender <strong>la clave de reflection en Go</strong> tendremos que entender c&oacute;mo funcionan, <code>Type</code>, <code>Kind</code> y <code>Value</code>.</p>
<p>Como siempre lo veremos con ejemplos pr&aacute;cticos.</p>
<p><img src="http://betabeers.com/uploads/blog/20190516_reflection_1.png" alt="reflection 1" width="500" height="496" /></p>
<p>El resultado de ejecutar este c&oacute;digo es el siguiente:</p>
<pre>main.Gopher
struct
{Adam Blue 20}
</pre>
<p>Playground: <a href="https://play.golang.org/p/oZsAi_xfzV7">https://play.golang.org/p/oZsAi_xfzV7</a></p>
<p>En el anterior c&oacute;digo vemos tres m&eacute;todos que pertenecen al <strong>paquete reflect</strong> vamos a explicar qu&eacute; hace cada uno.</p>
<ul>
<li><strong>reflect.TypeOf()</strong> espera un <code>interface{}</code> como par&aacute;metro y nos devuelve un <code>reflect.Type</code> es decir conoceremos el tipo de la interfaz que le estamos pasando, en este caso un <code>Gopher</code>.</li>
<li><strong>Kind()</strong> a partir de un <code>reflect.Type</code> sabremos la clase de tipo que es, en este caso <code>struct</code>.</li>
<li><strong>reflect.ValueOf()</strong> dado un <code>interface{}</code> podremos averiguar su valor.</li>
</ul>
<p>Hay que tener en cuenta que aunque utilizando el <code>fmt.Println(rValue)</code> nos devuelve el valor de nuestra <code>struct</code> &eacute;ste no podr&aacute; ser utilizado sin ser previamente casteado, ya que realmente no es un <code>Gopher</code> lo que devuelve sino un <code>reflect.Value</code>. Ve&aacute;moslo mejor en un ejemplo:</p>
<pre><code class="go language-go">...
name := rValue.Name
fmt.Println(name)
...
</code></pre>
<p>Si ejecutamos el c&oacute;digo anterior tendremos un error como &eacute;ste:</p>
<pre>prog.go:18:16: rValue.Name undefined (type reflect.Value has no field or method Name)
</pre>
<p>Pero podemos realizar el casteo correspondiente y poder acceder a los m&eacute;todos y propiedades de nuestro <code>struct</code></p>
<pre><code class="go language-go">...
name := rValue.Interface().(Gopher).Name
fmt.Println(name)
...
</code></pre>
<h2 id="inspeccionandoconreflection">Inspeccionando con reflection</h2>
<p><strong>Reflection</strong> nos permite hacer todav&iacute;a m&aacute;s, podemos llegar a obtener informaci&oacute;n de cu&aacute;ntos campos tiene una <code>struct</code> y de que tipo es cada campo. Si la variable es un <code>slice</code>, <code>channel</code>, <code>puntero</code>, <code>map</code> o <code>array</code>, tambi&eacute;n nos ofrece m&eacute;todos para poder examinar su tipo y su contenido.</p>
<p>Vamos a ver c&oacute;mo poner todo esto en pr&aacute;ctica con un ejemplo.</p>
<p><img src="http://betabeers.com/uploads/blog/20190516_reflection_2.png" alt="reflection 2" width="500" height="411" /></p>
<p>El resultado de la ejecuci&oacute;n es:</p>
<pre>Type: Gopher Kind: struct
Type: main.Gopher Kind: ptr
</pre>
<p>Playground: <a href="https://play.golang.org/p/8M7e_aRhMJZ">https://play.golang.org/p/8M7e_aRhMJZ</a></p>
<p>En el ejemplo anterior ya empezamos a ver algunas diferencias con lo que vimos previavemente, y es que la forma que tiene de comportarse <strong>reflection</strong> si es un <code>struct</code> o es otro de los tipos mencionados al principio del art&iacute;culo es relativamente diferente.</p>
<p>Podemos ver que cuando es un <code>struct</code> podemos llamar al m&eacute;todo <code>Name()</code> que ofrece <code>reflect.Type</code>, y &eacute;ste nos devuelve el nombre de nuestro <code>struct</code> pero <strong>si hici&eacute;ramos eso con nuestro acceso a memoria posterior obtendr&iacute;amos un string vac&iacute;o</strong>, as&iacute; que tenemos que utilizar el m&eacute;todo <code>Elem()</code> el cual ya si que nos dar&aacute; la informaci&oacute;n.</p>
<p>Antes tambi&eacute;n comentamos que pod&iacute;amos conocer el n&uacute;mero de argumentos de un <code>struct</code> y conocer el tipo de cada campo que lo compone, &iquest;c&oacute;mo lo hacemos?</p>
<p><img src="http://betabeers.com/uploads/blog/20190516_reflection_3.png" alt="reflection 3" width="700" height="432" /></p>
<p>El resultado de la ejecuci&oacute;n es:</p>
<pre>Field Type: Name: string Kind: string
Field Type: Color: string Kind: string
Field Type: Year: int Kind: int
</pre>
<p>Playground: <a href="https://play.golang.org/p/maDvcoIY4r6">https://play.golang.org/p/maDvcoIY4r6</a></p>
<p>Pues s&iacute;, como vemos un m&eacute;todo tan simple como <code>NumField()</code> nos permite acceder al n&uacute;mero de elementos que se compone nuestro <code>struct</code> a trav&eacute;s de su <code>reflect.Type</code>, una vez tenemos eso simplemente podemos acceder a ellos mediante un &iacute;ndice y el m&eacute;todo <code>Field(i int)</code> el cual nos devuelve un <code>reflect.Value</code>.</p>
<p>Vemos tambi&eacute;n que a la hora de imprimir su informaci&oacute;n tenemos que acceder a trav&eacute;s del atributo <code>Type</code> el cual ya nos ofrece, su tipo(<code>Type</code>) y su clase(<code>Kind</code>), adem&aacute;s el atributo <code>Name</code> nos permite conocer el mismo del atributo en cuesti&oacute;n de nuestro <code>struct</code>.</p>
<p>Pero podemos llegar a&uacute;n m&aacute;s lejos, en la inspecci&oacute;n incluso podr&iacute;amos conocer los <code>tags</code> que tienen nuestros atributos.</p>
<pre><code class="go language-go">...
if field.Tag != "" {
    fmt.Printf("Tag json: %s\n", field.Tag.Get("json"))
}
...
</code></pre>
<p>Si volvemos a ejecutar nuestro script a&ntilde;adiendo ese bloque if, tendremos el siguiente resultado (tambi&eacute;n hay que modificar el struct a&ntilde;adiendo el tag):</p>
<pre>Tag json: color,omitempty
</pre>
<p>Playground: <a href="https://play.golang.org/p/_-MQ9aUKF8U">https://play.golang.org/p/_-MQ9aUKF8U</a></p>
<p>Incluso podr&iacute;amos ver como acceder a la informaci&oacute;n de otro <code>struct</code> si uno de nuestros atributos es de tipo <code>struct</code>. Simplemente tendr&iacute;amos que comprobar que el atributo sea de <code>Kind()</code> <code>struct</code>, <code>field.Type.Kind() == reflect.Struct</code> y recorrerlo igual que hacemos con el <code>struct</code> principal.</p>
<h2 id="modificandomediantereflection">Modificando mediante reflection</h2>
<p>Al principio del art&iacute;culo comentabamos que adem&aacute;s de inspeccionar podr&iacute;amos modificar, nuestras variables, y adem&aacute;s os adelanto que podemos tambien crear en tiempo de ejecuci&oacute;n nuevas instancias de ellas.</p>
<p>Para poder modificar nuestras variables, <strong>simplemente necesitaremos un puntero hacia ellas</strong> y el paquete <code>reflect</code> nos dar&aacute; todo lo necesario para lo dem&aacute;s.</p>
<p><img src="http://betabeers.com/uploads/blog/20190516_reflection_4.png" alt="reflection 4" width="700" height="734" /></p>
<p>Si ejecutamos esta pieza de c&oacute;digo obtendremos el siguiente resultado:</p>
<pre>{Adam Blue 20}
I'm a gopher, and I will be modified via reflection
**********************
{Killgrave Purple 55}
Modified string
**********************
Hi I'm Brian, my color is Red and I've 34 years
</pre>
<p>Playground: <a href="https://play.golang.org/p/b_dZqAtyv2o">https://play.golang.org/p/b_dZqAtyv2o</a></p>
<p>Aunque por el ejemplo puede llegar a intimidar, realmente hemos introducido pocas cosas nuevas, vamos a analizar el c&oacute;digo.</p>
<p>Como coment&aacute;bamos para poder modificar nuestras variables, lo primero que necesitaremos ser&aacute; punteros a ellas y adem&aacute;s obtener sus <code>reflect.Value</code> respectivos. Una vez tenemos los <code>reflect.Value</code> simplemente tendremos que modificarlos utilizando para ello <code>Elem().Set(v Value)</code> es decir necesitaremos un <code>reflect.Value</code> con nuestro nuevo valor, pero si es un tipo b&aacute;sico vemos que esto no es necesario ya que el paquete <code>reflect</code> nos ofrece m&eacute;todos como <code>SetString</code>, <code>SetInt</code>, <code>SetBool</code>, entre otros.</p>
<p>As&iacute; pues una vez volvemos a imprimir nuestras variables, veremos qu&eacute; tienen los nuevos datos que les hemos seteado.</p>
<p>Pero ah&iacute; no queda la magia sino que a trav&eacute;s del <code>reflect.New</code> podremos crear una instancia completamente nueva a partir de un <code>reflect.Type</code> e inicilizar sus valores como hemos aprendido anteriormente, despu&eacute;s solo tendremos que convertir nuestro <code>reflect</code> en el tipo que necesitamos, y &iexcl;voil&agrave;, un mundo de posibilidades se abre ante nosotros!</p>
<h2 id="conclusin">Conclusi&oacute;n</h2>
<p>Hemos dado unas pinceladas a las posibilidades que ofrece <strong>reflection</strong>, a&uacute;n queda mucho m&aacute;s por profundizar y aprender, pero ya ten&eacute;is una base para poder empezar a utilizar esta gran utilidad, aunque como bien dijo Ben Parker, en su d&iacute;a un gran poder conlleva una gran responsabilidad y es que usar <strong>reflection</strong> es un poco delicado, recordemos que se usa en tiempo de ejecuci&oacute;n con lo cual que nuestro c&oacute;digo compile no quiere decir que no podamos encontrarnos errores a la hora ejecutarlo, y recordemos en muchos casos incluso podremos obtener <code>panic</code>.</p>
<p>As&iacute; que utiliza <strong>reflection</strong> con mucho cuidado y siempre que de verdad lo necesites, piensa que muchas veces que creas que necesitas utilizar <strong>reflection</strong> para solucionar tu problema posiblemente sea porque lo estes planteando mal, o estes pensando como hacer las cosas como en alg&uacute;n lenguaje no tipado.</p>
<p>Recuerda que si tienes cualquier duda o sugerencia, puedes dejarlo en los comentarios o v&iacute;a nuestro twitter <a href="https://twitter.com/friendsofgotech">FriendsOfGo</a></p>]]></content:encoded>
</item><item>
<title><![CDATA[Controles previos antes del lanzamiento de un sitio web WordPress]]></title>
<link>https://betabeers.com/blog/controles-previos-antes-del-lanzamiento-un-sitio-web-wordpress-404/</link>
<pubDate>Fri, 10 May 2019 12:07:57 +0000</pubDate>
<category>Colaboraciones</category>
<content:encoded><![CDATA[<p>La proliferaci&oacute;n de herramientas para la maquetaci&oacute;n visual o plataformas de publicaci&oacute;n web pensadas para el usuario final pueden hacernos pensar que el lanzamiento de un sitio web es algo tan sencillo como simplemente pulsar el bot&oacute;n de publicar en el &uacute;ltimo de los contenidos creados.</p>
<p>Pero nada m&aacute;s lejos de la realidad, si trabajamos en proyectos web desde cero, haciendo un seguimiento de cada una de sus fases, sabremos que cada detalle cuenta y que tiene mucho que ver con el &eacute;xito o el fracaso de un negocio digital.</p>
<p><img style="display: block; margin-left: auto; margin-right: auto;" title="Controles previos lanzamiento WordPress" src="http://betabeers.com/uploads/blog/20190510_controles-previos-lanzamiento-wordpress.jpg" alt="Controles previos lanzamiento WordPress" width="400" height="267" /></p>
<p>En este caso, analizaremos la &uacute;ltima de las fases, concretamente el momento previo al lanzamiento de un sitio web teniendo en cuenta las &aacute;reas en las cuales debemos fijarnos como dise&ntilde;adores o desarrolladores web a la hora de completar un trabajo con la calidad que se merece.</p>
<h2>Controles de calidad previos al lanzamiento</h2>
<p>Nos referimos a todas las pruebas, automatizadas o no, que deber&iacute;amos poner a disposici&oacute;n de un proyecto para asegurarnos de que el resultado va a ser el esperado tanto para el usuario final como para el cliente o los encargados de la gesti&oacute;n del sitio web.</p>
<p>Es posible que la persona del equipo que se encargue de realizar estas pruebas, no tenga un rol t&eacute;cnico, pero desde nuestro punto de vista, deber&iacute;amos conocer los aspectos a revisar para garantizar que no sea necesaria ninguna revisi&oacute;n posterior del trabajo una vez haya alcanzado esta fase o incluso haya sido definitivamente publicado.</p>
<h3>Legibilidad</h3>
<p>Es habitual centrarnos en el aspecto t&eacute;cnico de un proyecto especialmente si entra&ntilde;a alg&uacute;n tipo de dificultad, pero esa perspectiva no debe desviarnos del foco del usuario. En el mismo proceso de maquetaci&oacute;n y publicaci&oacute;n de contenidos, deber&iacute;amos prestar atenci&oacute;n a algunos puntos:</p>
<ul>
<li>Errores gramaticales</li>
<li>Longitud adecuada de los textos para su lectura</li>
<li>Errores en la disposici&oacute;n del contenido a trav&eacute;s de p&aacute;rrafos, listados u otros elementos</li>
</ul>
<p>En definitiva, leer la web que estamos publicando, y en el sentido m&aacute;s literal de la expresi&oacute;n.</p>
<p>Una herramienta que puede ayudarnos a detectar textos que no son legibles o atractivos en formato web es <a href="http://www.hemingwayapp.com/" rel="noopener" target="_blank">Hemingway App</a>. No est&aacute; tan afinada en castellano como en ingl&eacute;s, pero puede darnos una primera aproximaci&oacute;n.</p>
<h3>Navegadores web</h3>
<p>Otro de los errores m&aacute;s comunes que solemos cometer en la fase de desarrollo es el de no comprobar la ejecuci&oacute;n del sitio web en navegadores diferentes al que usamos habitualmente. Creemos que el hecho de que algo funcione en nuestro ecosistema de trabajo es garant&iacute;a suficiente de que vaya a hacerlo correctamente en el resto.</p>
<p>Es importante comprobar la compatibilidad con el resto de navegadores y versiones de los mismos, al menos hasta donde la l&oacute;gica nos permita llegar.</p>
<p>Herramientas como <a href="https://www.browserstack.com/" rel="noopener" target="_blank">BrowserStack</a> permiten que podamos realizar estas pruebas de manera m&aacute;s sencilla e incluso automatizada en su gran parte.</p>
<h3>Dispositivos m&oacute;viles</h3>
<p>Si el punto anterior es todo un cl&aacute;sico en los tests de resoluci&oacute;n web, el apartado de los dispositivos m&oacute;viles tambi&eacute;n lo est&aacute; siendo desde la irrupci&oacute;n de los smartphones en nuestras vidas.</p>
<p>Aunque en muchos sectores el porcentaje de navegaci&oacute;n m&oacute;vil supera con creces al de navegaci&oacute;n desde escritorio, a&uacute;n puede costarnos cambiar esa tendencia y pensar en mobile first en todos nuestros proyectos. Desde la elecci&oacute;n del tema o el desarrollo del mismo deber&iacute;amos estar enfocados en que el usuario debe consumir la informaci&oacute;n en dispositivos m&oacute;viles de un modo adecuado.</p>
<p>Nos centraremos en algunos puntos:</p>
<ul>
<li>Organizaci&oacute;n de la informaci&oacute;n adecuada para el dispositivo</li>
<li>Usabilidad en las acciones deseadas en el sitio web</li>
<li>An&aacute;lisis del comportamiento m&oacute;vil para adecuar el dise&ntilde;o</li>
</ul>
<p>La mejor de las herramientas que tenemos a nuestro alcance es nuestro propio terminal m&oacute;vil que deber&iacute;amos tener siempre a mano para poder realizar las primeras comprobaciones.</p>
<p>Y otro de los cl&aacute;sicos en fases de testing, es <a href="https://chrispederick.com/work/web-developer/" rel="noopener" target="_blank">Web Developer</a>, una de las herramientas m&aacute;s intuitivas que re&uacute;ne herramientas para desarrolladores y varios apartados con los que comprobar c&oacute;mo se visualizar&iacute;a un sitio web en diferentes pantallas.</p>
<h3>Rendimiento</h3>
<p>Cuando hablamos de rendimiento nos referimos a diferentes aspectos que van de la mano y que permiten que la experiencia de usuario sea satisfactoria: velocidad de carga en la web, procesos &aacute;giles en el sitio web y reducci&oacute;n de consumos.</p>
<p>Deber&iacute;amos tener la optimizaci&oacute;n del rendimiento del sitio web o WPO, no solo por experiencia de usuario, sino tambi&eacute;n por ahorro de costes, posicionamiento web y pensando en la accesibilidad.</p>
<p>Las herramientas que m&aacute;s informaci&oacute;n nos ofrecen desde un punto de vista objetivo son aquellas que miden aspectos del rendimiento de la web como <a href="https://developers.google.com/speed/pagespeed/insights/">PageSpeed Insights</a>, <a href="https://tools.pingdom.com/">Pingdom Tools</a>, o <a href="https://gtmetrix.com/">GTMetrix</a>.</p>
<h3>Usabilidad</h3>
<p>Si lo que queremos es estar cien por cien seguros del resultado de nuestro proyecto, el factor humano no puede ser desechado. Cada vez existen m&aacute;s servicios a nuestro alcance que nos permiten realizar test de usabilidad con usuarios reales ofreciendo valoraciones y contenido real de la navegaci&oacute;n web para poder sacar conclusiones.Algunos servicios que cada vez son m&aacute;s demandado pueden ser los de <a href="https://www.inspectlet.com/">Inspectlet </a>o <a href="https://www.usertesting.com/">User Testing</a>.</p>
<h3>Auditor&iacute;a SEO</h3>
<p>Disponemos de herramientas gratuitas como Google Analytics o Google Search Console que nos sirven para monitorizar lo que est&aacute; sucediendo en el sitio web una vez lanzado a producci&oacute;n aunque para la fase previa deberemos echar mano de otros servicios.</p>
<p>Uno de los plugins m&aacute;s interesantes para detectar enlaces rotos en un sitio web creado con WordPress es <a href="https://wordpress.org/plugins/broken-link-checker/">Broken Link Checker</a>. Si buscamos herramientas externas podemos contar con la ayuda de <a href="https://www.screamingfrog.co.uk/seo-spider/">Screaming Frog</a>.</p>
<p>Si preferimos utilizar herramientas desde el navegador web, <a href="https://chrome.google.com/webstore/detail/seo-meta-in-1-click/bjogjfinolnhfhkbipphpdlldadpnmhc">SEO in 1 Click</a> es una de las m&aacute;s intuitivas para analizar etiquetado HTML y otros elementos relevantes para el SEO On Page de una web.</p>
<h2>Conclusiones</h2>
<p>Cuando realizamos este tipo de controles de calidad previos al lanzamiento de un sitio web solemos tender a revisar todo aquello que nos encontramos a posteriori, una vez cerrado el proyecto completo.</p>
<p>Mi recomendaci&oacute;n es realizar este seguimiento cada vez que publicamos una secci&oacute;n o p&aacute;gina de un proyecto para repartir la inversi&oacute;n de tiempo en las revisiones y poder atajar con el tiempo suficiente cualquier deficiencia que encontremos.</p>
<p>Es responsabilidad de quien est&aacute; creando cada uno de los contenidos, prestar atenci&oacute;n a cada uno de los puntos comentados evitando al m&aacute;ximo revisiones posteriores y trabajo a&ntilde;adido en consecuencia.</p>
<p>En la mayor parte de ocasiones resulta menos tedioso y conlleva un ahorro de tiempo considerable, tener listo un plan de acci&oacute;n antes de entregar un parcial de un proyecto web, as&iacute; que aunque solo sea por ahorro de tiempo y energ&iacute;a, los pasos comentados deber&iacute;an estar dentro de nuestras funciones como desarrolladores o maquetadores.</p>]]></content:encoded>
</item><item>
<title><![CDATA[Architecture Components: Paging Library (Parte II) #AndroidMeetsKotlin]]></title>
<link>https://betabeers.com/blog/architecture-components-paging-library-parte-ii-androidmeetskotlin-402/</link>
<pubDate>Fri, 03 May 2019 00:28:58 +0000</pubDate>
<category>Colaboraciones</category>
<content:encoded><![CDATA[<h1 id="h.24j1vygkxbis" class="c7"><img style="display: block; margin-left: auto; margin-right: auto;" src="http://betabeers.com/uploads/blog/20190503_imagen_1.jpg" alt="" width="500" height="275" /></h1>
<p class="c0">Siguiendo el <a class="c2" href="https://www.google.com/url?q=https://betabeers.com/blog/architecture-components-pagin-library-parte-i-androidmeetskotlin-399/&amp;sa=D&amp;ust=1556838490736000">anterior post</a> sobre integraci&oacute;n de Paging Library, ahora vas a llegar al 100% de la integraci&oacute;n. De esta forma, ver&aacute;s las modificaciones en el repositorio, ViewModel, la creaci&oacute;n del <a class="c2" href="https://www.google.com/url?q=https://developer.android.com/reference/android/arch/paging/PagedListAdapter&amp;sa=D&amp;ust=1556838490736000">PagedListAdapter</a>, la Activity y finalmente, la modificaci&oacute;n del servicio para a&ntilde;adir el n&uacute;mero de p&aacute;gina. Como siempre, el c&oacute;digo del proyecto puedes encontrarlo en el siguiente <a class="c2" href="https://www.google.com/url?q=https://github.com/franlopjur/ArchitectureComponentsSample/tree/paging&amp;sa=D&amp;ust=1556838490737000">enlace</a> bajo la rama &ldquo;paging&rdquo;. Sin m&aacute;s, &iexcl;Al l&iacute;o!</p>
<p class="c0 c3"> </p>
<h3 id="h.vr6e3kqtf20k" class="c4">1.- Incluye el n&uacute;mero de p&aacute;gina y los elementos por p&aacute;gina</h3>
<p class="c0">Debes modificar:</p>
<h4 id="h.uy37yj6ulwny" class="c13">1.1.- GithubService</h4>
<p class="c0">El servicio ahora recibir&aacute; los dos nuevos par&aacute;metros referidos a la paginaci&oacute;n (&ldquo;page&rdquo; y &ldquo;per_page&rdquo;) por lo que quedar&iacute;a de la siguiente forma:</p>
<p class="c0"><img src="http://betabeers.com/uploads/blog/20190503_imagen_2.png" alt="" width="600" height="67" /></p>
<p class="c0 c3"> </p>
<p class="c0">La implementaci&oacute;n la puedes encontrar en el siguiente <a class="c2" href="https://www.google.com/url?q=https://github.com/franlopjur/ArchitectureComponentsSample/blob/paging/app/src/main/java/com/betabeers/architecturecomponentsexample/api/GithubService.kt&amp;sa=D&amp;ust=1556838490738000">enlace</a>.</p>
<h4 id="h.xoxv12v9spoj" class="c13">1.2.- GithubRepoApiDataSource</h4>
<p class="c0">El m&eacute;todo &ldquo;getGithubRepos&rdquo; tambi&eacute;n debe recibir estos par&aacute;metros por lo que tienes que cambiar su declaraci&oacute;n y la llamada al m&eacute;todo &ldquo;searchRepositories&rdquo; anteriormente comentado.</p>
<p class="c0 c3"> </p>
<p class="c0"><img src="http://betabeers.com/uploads/blog/20190503_imagen_3.png" alt="" width="500" height="67" /></p>
<p class="c0 c3"> </p>
<p class="c0">El n&uacute;mero de p&aacute;gina ser&aacute; constante (ITEMS_PER_PAGE_NETWORK) y tendr&aacute; un valor de 50.</p>
<p class="c0 c3"> </p>
<p class="c0">La implementaci&oacute;n la puedes encontrar en el siguiente <a class="c2" href="https://www.google.com/url?q=https://github.com/franlopjur/ArchitectureComponentsSample/blob/paging/app/src/main/java/com/betabeers/architecturecomponentsexample/data/GithubRepoApiDataSource.kt&amp;sa=D&amp;ust=1556838490738000">enlace</a>.</p>
<h3 id="h.943w9hvd7pen" class="c4">2.- Cambios en el Repositorio (GithubRepository)</h3>
<p class="c0">Este cambio ser&aacute; uno de los mayores que vas a realizar, por lo tanto debes seguir los siguientes pasos:</p>
<p class="c0 c3"> </p>
<h4 id="h.58ool6jog3an" class="c13">2.1.- A&ntilde;adir nuevas variables a la clase</h4>
<p class="c0">Debes a&ntilde;adir:</p>
<ul class="c10 lst-kix_99qwcy36jp18-0 start">
<li class="c0 c9"><strong>var callback: GithubRepoBoundaryCallback</strong>. Servir&aacute; para la actualizaci&oacute;n de los nuevos datos necesarios a mostrar.</li>
<li class="c0 c9">v<strong>ar lastQueryInfoRequested: String</strong>. Tendr&aacute; la informaci&oacute;n de la &uacute;ltima consulta realizada, de esta forma, si la consulta es distinta, se resetear&aacute; el n&uacute;mero de p&aacute;gina consultado.</li>
</ul>
<h4 id="h.js2m827o7gbb" class="c13">2.2.- A&ntilde;ade los m&eacute;todos &ldquo;isSameQueryThanLastQuery&rdquo; y &ldquo;setUpLastPageNumberToQuery&rdquo;</h4>
<ul class="c10 lst-kix_ydjs64x76y9d-0 start">
<li class="c0 c9"><strong>isSameQueryThanLastQuery</strong>. Como su nombre indica, comprobar&aacute; si la nueva consulta es id&eacute;ntica a la anteriormente consultada.</li>
</ul>
<p class="c0 c3"> </p>
<p class="c0"><img src="http://betabeers.com/uploads/blog/20190503_imagen_4.png" alt="" width="500" height="72" /></p>
<p class="c0 c3"> </p>
<ul class="c10 lst-kix_ydjs64x76y9d-0">
<li class="c0 c9"><strong>setUpLastPageNumberToQuery</strong>. Establecer&aacute; el n&uacute;mero de p&aacute;gina a consultar en base a los datos que ya hay almacenados en la base de datos.</li>
</ul>
<p class="c0"><img src="http://betabeers.com/uploads/blog/20190503_imagen_5.png" alt="" width="600" height="108" /></p>
<h4 id="h.mvafsb1xnlfo" class="c13">2.3.- Actualiza el m&eacute;todo &ldquo;searchRepos&rdquo;</h4>
<p class="c0">Antes, este m&eacute;todo s&oacute;lo consultaba los datos al servicio y actualizaba los datos una vez eran obtenidos. Ahora, debes comprobar si la consulta es igual a la anterior o no, para configurar el <a class="c2" href="https://www.google.com/url?q=https://developer.android.com/reference/android/arch/paging/PagedList.BoundaryCallback&amp;sa=D&amp;ust=1556838490740000">BoundaryCallback</a> que se encargar&aacute; de rellenar tu listado.</p>
<p class="c0">Como segundo paso, debes obtener el DataSource.Factory desde el DataSource de Local y configurar el <a class="c2" href="https://www.google.com/url?q=https://developer.android.com/reference/android/arch/paging/PagedList.Config&amp;sa=D&amp;ust=1556838490741000">PagedList.Config</a>.</p>
<p class="c0">La configuraci&oacute;n ser&aacute; la siguiente:</p>
<ul class="c10 lst-kix_ld5fgdxq5kl5-0 start">
<li class="c0 c9">&ldquo;<strong>EnablePlaceholders</strong>&rdquo; = true.</li>
<li class="c0 c9">&ldquo;<strong>InitialLoadSizeHint</strong>&rdquo; = Constante ITEMS_PER_PAGE_DB con valor 20.</li>
<li class="c0 c9">&ldquo;<strong>PageSize</strong>&rdquo; = Constante ITEMS_PER_PAGE_DB con valor 50.</li>
<li class="c0 c9">&ldquo;<strong>PrefetchDistance</strong>&rdquo; = Constante PREFETCH_DISTANCE con valor 5.</li>
</ul>
<p class="c0">Finalmente, una vez has creado los anteriores objetos, tienes que crear el LivePagedListBuilder, establecer el factory, config y callback creados anteriormente. Por &uacute;ltimo, llama a su m&eacute;todo build() y ret&oacute;rnalo.</p>
<p class="c0 c3"> </p>
<p class="c0">La implementaci&oacute;n quedar&iacute;a de la siguiente forma y la puedes encontrar en el siguiente <a class="c2" href="https://www.google.com/url?q=https://github.com/franlopjur/ArchitectureComponentsSample/blob/paging/app/src/main/java/com/betabeers/architecturecomponentsexample/data/GithubRepository.kt&amp;sa=D&amp;ust=1556838490742000">enlace</a>:</p>
<p class="c0"><img src="http://betabeers.com/uploads/blog/20190503_imagen_6.png" alt="" width="550" height="351" /></p>
<p class="c0 c3"> </p>
<h3 id="h.mrtm37bt388z" class="c4">3.- Cambios en el ViewModel (SearchRepoViewModel)</h3>
<p class="c0">Realizar&aacute;s dos cambios:</p>
<ul class="c10 lst-kix_s3gzmkjx9gsp-0 start">
<li class="c0 c9">La variable githubReposListLiveData tiene que pasar de tipo LiveData&lt;List&lt;GithubRepoDomain&gt;&gt; a LiveData&lt;PagedList&lt;GithubRepoDomain&gt;&gt;</li>
<li class="c0 c9">El m&eacute;todo &ldquo;search&rdquo; debe retornar LiveData&lt;PagedList&lt;GithubRepoDomain&gt;&gt; ya que lo usar&aacute;s en la declaraci&oacute;n de la variable anterior haciendo uso del m&eacute;todo &ldquo;Transformations.switchMap&rdquo;.</li>
</ul>
<p class="c0 c3"> </p>
<p class="c0">La implementaci&oacute;n la puedes encontrar en el siguiente <a class="c2" href="https://www.google.com/url?q=https://github.com/franlopjur/ArchitectureComponentsSample/blob/paging/app/src/main/java/com/betabeers/architecturecomponentsexample/ui/viewmodel/SearchRepoViewModel.kt&amp;sa=D&amp;ust=1556838490743000">enlace</a>.</p>
<h3 id="h.j8o1ao32j3hf" class="c4">4.-Creaci&oacute;n del PagedListAdapter (GithubRepoAdapter)</h3>
<p class="c0">El adapter es muy similar a cualquiera de los que has creado anteriormente, s&oacute;lo tiene un &uacute;nico cambio y debe extender de PagedListAdapter. Ten en cuenta que su constructor recibe un <a class="c2" href="https://www.google.com/url?q=https://developer.android.com/reference/android/support/v7/util/DiffUtil.ItemCallback&amp;sa=D&amp;ust=1556838490743000">DiffUtil.ItemCallback</a> que comprueba si los elementos son iguales o si el contenido de los mismos es id&eacute;ntico.</p>
<p class="c0 c3"> </p>
<p class="c0">Para la implementaci&oacute;n del DiffUtil debes crear un &ldquo;companion object&rdquo; y quedar&iacute;a tal que as&iacute;:</p>
<p class="c0"><img src="http://betabeers.com/uploads/blog/20190503_imagen_7.png" alt="" width="550" height="226" /></p>
<p class="c0 c3"> </p>
<p class="c0">La implementaci&oacute;n del resto del adapter puedes encontrarla en el siguiente <a class="c2" href="https://www.google.com/url?q=https://github.com/franlopjur/ArchitectureComponentsSample/blob/paging/app/src/main/java/com/betabeers/architecturecomponentsexample/ui/adapter/GithubRepoAdapter.kt&amp;sa=D&amp;ust=1556838490744000">enlace</a>.</p>
<h3 id="h.lg0zf1h846eh" class="c4">5.- Cambios en la Activity (MainActivity)</h3>
<p class="c0">S&oacute;lo debes realizar un &uacute;nico cambio, el Observer debe pasar de ser &ldquo;Observer&lt;List&lt;GithubRepoDomain&gt;&gt;&rdquo; a &ldquo;Observer&lt;PagedList&lt;GithubRepoDomain&gt;&gt;&rdquo;.</p>
<p class="c0">Adem&aacute;s, debes crear el adapter y vincularlo al RecyclerView, lo que har&aacute; posible visualizar los datos y ver si la paginaci&oacute;n funciona correctamente.</p>
<p class="c0">La implementaci&oacute;n de c&oacute;mo quedar&iacute;a puedes verla en el siguiente <a class="c2" href="https://www.google.com/url?q=https://github.com/franlopjur/ArchitectureComponentsSample/blob/paging/app/src/main/java/com/betabeers/architecturecomponentsexample/ui/activity/MainActivity.kt&amp;sa=D&amp;ust=1556838490745000">enlace</a>.</p>
<p class="c0 c3"> </p>
<h2 id="h.q3rizvi0m7l8" class="c15">Se termin&oacute;</h2>
<p class="c0">En resumen, si has seguido los pasos minuciosamente detallados en este y el <a class="c2" href="https://www.google.com/url?q=https://betabeers.com/blog/architecture-components-pagin-library-parte-i-androidmeetskotlin-399/&amp;sa=D&amp;ust=1556838490745000">anterior post</a> (Architecture Components: Paging Library (Parte I) #AndroidMeetsKotlin de Fran L&oacute;pez), habr&aacute;s pasado de tener una simple carga de datos fija a una paginaci&oacute;n din&aacute;mica y r&aacute;pida. Esta librer&iacute;a simplifica mucho el desarrollo e integraci&oacute;n de este tipo de comportamientos y hace mucho m&aacute;s f&aacute;cil su implementaci&oacute;n. En definitiva, Architecture Components sigue haci&eacute;ndote la vida mucho m&aacute;s f&aacute;cil.</p>
<p class="c0 c3"> </p>
<p class="c0 c3"> </p>]]></content:encoded>
</item><item>
<title><![CDATA[Document Provider en Android: Introducción]]></title>
<link>https://betabeers.com/blog/document-provider-android-introduccion-401/</link>
<pubDate>Fri, 26 Apr 2019 10:35:38 +0000</pubDate>
<category>Noticias</category>
<content:encoded><![CDATA[<p><em>Este art&iacute;culo ha sido escrito por Abel Garcia y publicado originalmente en el blog de <a href="https://solidgeargroup.com/que-es-un-document-provider?lang=es" target="_blank">Solid GEAR</a>.</em></p>
<p style="text-align: center;"><em><img src="https://solidgeargroup.com/wp-content/uploads/2019/04/document_provider.jpg" alt="" /></em></p>
<p>Antes de la introducci&oacute;n del Document Provider, si necesit&aacute;bamos crear un gestor de documentos, ya fuera para archivos locales, dispositivos de almacenamiento o almacenamiento en Cloud, ten&iacute;amos que crear una interfaz completa con sus vistas, adaptadores, los men&uacute;s de opciones, la multiselecci&oacute;n etc. Al fin y al cabo, cada gestor de documentos ten&iacute;a su propia interfaz y el usuario ten&iacute;a que aprender a utilizar todas y cada una de ellas. El Battle Royale de los gestores de documentos.</p>
<p>Pero todo cambi&oacute; con la llegada de Android KitKat (API 19) y la introducci&oacute;n del Storage Access Framework y con ello, el Document Provider. Esto permiti&oacute; a los usuarios tener una sola interfaz para los ficheros, sin importar si son locales o de un almacenamiento en la nube. Una interfaz para unirlas a todas.</p>
<h4><strong><em>Pero, &iquest;qu&eacute; es en realidad un Document Provider?</em></strong></h4>
<p>La clave de todo es que la interfaz la genera el sistema en vez de nuestra aplicaci&oacute;n. Con esto, solo nos tendremos que preocupar de proveer de la informaci&oacute;n necesaria al Document Provider para que muestre en la interfaz los directorios o ficheros de nuestra aplicaci&oacute;n. Todo esto lo facilita la API del <a href="https://developer.android.com/reference/android/provider/DocumentsProvider.html" rel="noreferrer noopener" target="_blank">DocumentsProvider</a>. Sin m&aacute;s dilaci&oacute;n, vamos al turr&oacute;n.</p>
<h4>Lo a&ntilde;adimos al Manifest</h4>
<p>Lo primero que tendremos que hacer, ser&aacute; registrarlo en nuestro manifest y tendr&aacute; un aspecto tal que as&iacute;:</p>
<div class="EnlighterJSWrapper classicEnlighterJSWrapper"><ol class="hoverEnabled classicEnlighterJS EnlighterJS">
<li class=" odd">&lt;provider</li>
<li class=" even">android:name="com.example.MyCloudProvider"</li>
<li class=" odd">android:authorities="com.example.mycloudprovider"</li>
<li class=" even">android:exported="true"</li>
<li class=" odd">android:grantUriPermissions="true"</li>
<li class=" even">android:permission="android.permission.MANAGE_DOCUMENTS"</li>
<li class=" odd">android:enabled="@bool/isAtLeastKitKat"&gt;</li>
<li class=" even"></li>
<li class=" odd"></li>
<li class=" even"></li>
<li class=" odd"></li>
</ol><br />
<p>Tenemos toda la informaci&oacute;n relativa a los campos que necesitamos <a href="https://developer.android.com/guide/topics/providers/create-document-provider?hl=en#manifest" rel="noreferrer noopener" target="_blank">aqu&iacute;</a>, pero vamos a comentar algunos de ellos:</p>
<ul>
<li><strong>Authorities</strong>: String &uacute;nico que ser&aacute; el prefijo de todas las Uris que tengamos en nuestro document provider. Podr&iacute;amos crear un String y recuperarlo desde all&iacute;, o incluso a&ntilde;adirlo al gradle.build de la aplicaci&oacute;n para acceder de forma sencilla a este valor, porque vamos a utilizarlo posteriormente.</li>
<li><strong>Enabled</strong>: Si soportamos versiones previas a Kitkat, tendr&iacute;amos que a&ntilde;adir un boolean para determinar si estamos en una versi&oacute;n anterior a KitKat o no, tal y c&oacute;mo se ve en el c&oacute;digo superior. Si soportamos solo versiones superiores, podr&iacute;amos suprimir este campo, porque estar&aacute; habilitado por defecto.</li>
</ul>
<h4>Tarea previa: Los Document Contracts</h4>
<p>Antes de comenzar con la funcionalidad b&aacute;sica, vamos a recordar dos DocumentsContract que nos permitir&aacute;n construir la estructura que va a tener nuestro Document Provider.</p>
<p>En primer lugar el <a href="https://developer.android.com/reference/android/provider/DocumentsContract.Root.html" rel="noreferrer noopener" target="_blank">Root </a>que nos permitir&aacute; establecer los campos que tendr&aacute; nuestra ra&iacute;z de documentos. Podemos a&ntilde;adir m&aacute;s columnas, que aportar&aacute;n informaci&oacute;n adicional, pero lo m&iacute;nimo necesario para comenzar ser&iacute;a lo siguiente:</p>
<ul>
<li>COLUMN_ROOT_ID</li>
<li>COLUMN_ICON</li>
<li>COLUMN_TITLE</li>
<li>COLUMN_FLAGS</li>
<li>COLUMN_DOCUMENT_ID</li>
</ul>
<p>Cada ra&iacute;z comienza con un document_id que ser&aacute; el identificador &uacute;nico y superior de un &aacute;rbol de directorios, que puede ser una cuenta, o un tipo de almacenamiento etc y que podr&aacute; contener archivos o m&aacute;s directorios que se tratar&aacute;n con el siguiente Contract, el <a href="https://developer.android.com/reference/android/provider/DocumentsContract.Document.html" rel="noreferrer noopener" target="_blank">Document</a>.</p>
<p>Con esto, estableceremos la informaci&oacute;n para cada archivo. Lo m&iacute;nimo necesario para comenzar:</p>
<ul>
<li>COLUMN_DOCUMENT_ID</li>
<li>COLUMN_DISPLAY_NAME</li>
<li>COLUMN_MIME_TYPE</li>
<li>COLUMN_FLAGS</li>
<li>COLUMN_SIZE</li>
<li>COLUMN_LAST_MODIFIED</li>
</ul>
<p>Los Flags de ambos contracts nos indicar&aacute;n las operaciones disponibles para ese root o ese documento. Por ejemplo, si queremos permitir operaciones de creaci&oacute;n, renombrado o borrado de documentos, tendriamos que a&ntilde;adir los flags correspondientes.</p>
<h4>Comenzamos por la ra&iacute;z: <em>queryRoots</em></h4>
<p>En esta funci&oacute;n tendremos que devolver un cursor con la informaci&oacute;n relativa que queramos a&ntilde;adir al Root. Podemos a&ntilde;adir una nueva fila por cuenta, o incluso dividir las cuentas por tipo de documento que queramos mostrar: Im&aacute;genes, pdf, videos etc con los <a href="https://developer.android.com/reference/android/provider/DocumentsContract.Root.html#COLUMN_FLAGS" rel="noreferrer noopener" target="_blank">flags</a> que consideremos oportunos para cada uno de ellos.</p>
<p>De esta forma poblar&iacute;amos nuestro Root, y podr&iacute;amos conseguir que muestre algo similar a esto:</p>
<div class="wp-block-image"><img class="wp-image-7757" src="https://solidgeargroup.com/wp-content/uploads/2019/04/queryRoots.png" alt="Lista de Roots de un Documents Provider" width="270" height="465" />Listado de Roots <br />
<h4>Mostrar los documentos en el provider: <em>queryChildDocuments</em></h4>
<p>Ahora que hemos conseguido mostrar nuestra ra&iacute;z en el Provider, vamos a listar los documentos que hay en ella. Para ello usaremos la funci&oacute;n queryChildDocuments. Se llamar&aacute; a este m&eacute;todo cada vez que seleccionemos un directorio en nuestro provider.</p>
<p>Esta funci&oacute;n devolver&aacute; un MatrixCursor que tendremos que poblar con los ficheros que se encuentren en el directorio cuyo id recibimos por par&aacute;metro. A&ntilde;adiremos una fila por cada fichero siguiendo el Document Contract comentado anteriormente.</p>
<p>Si todo ha ido bien, podremos ver nuestra lista de archivos cuando seleccionemos nuestra ra&iacute;z:</p>
<div class="wp-block-image"><img class="wp-image-7755" src="https://solidgeargroup.com/wp-content/uploads/2019/04/queryChildDocuments.png" alt="Listado de los archivos que se encuentran en el directorio sobre el cual hacemos la consulta" width="270" height="465" />Lista de archivos contenidos en el directorio<br />
<h4>Apuntando a un documento: <em>queryDocument</em></h4>
<p>Ahora que tenemos nuestra lista de ficheros y directorios, nos interesa consultar cada uno de ellos. Para ello utilizaremos queryDocument.</p>
<p>Esta funci&oacute;n es similar a la anterior, pero solo para el documento espec&iacute;fico. Es decir, devolveremos un Cursor pero solo con los metadatos del documento que queremos consultar. Deber&iacute;a ser lo m&aacute;s r&aacute;pida posible (Sin peticiones de red ni nada por el estilo).</p>
<h4>Abriendo un Documento: <em>openDocument</em></h4>
<p>Para abrir un documento, tendremos que devolver un ParcelFileDescriptor que represente al fichero cuyo Id recibimos por par&aacute;metro. Adem&aacute;s del id, obtendremos tambi&eacute;n el modo por par&aacute;metro, ya sea lectura o escritura. Para hacerlo lo m&aacute;s sencillo posible, vamos a establecer solo modo lectura.</p>
<p>Con esto ya quedar&iacute;an resueltos todos las funciones m&iacute;nimas y necesarias para tener nuestro Document Provider funcional. Pero hay algunas cosas que comentar todav&iacute;a.</p>
<h4>Notificar al Document Provider de un cambio</h4>
<p>Podemos notificar al Document Provider de un cambio a&ntilde;adiendo al cursor un listener. De esta forma cuando actualicemos un archivo, o creamos un documento, notificamos al listener y actualizar&aacute; la vista.</p>
<p>Esto puede ahorrarnos costes innecesarios de sincronizaci&oacute;n tras cada operaci&oacute;n que realicemos y se realiza mediante Uris. Estas Uris se generar&aacute;n con la authority mencionada en el apartado del manifest. Podr&iacute;amos crear una funcion como esta para generar estas Uris:</p>
<div class="EnlighterJSWrapper classicEnlighterJSWrapper"><ol class="hoverEnabled classicEnlighterJS EnlighterJS">
<li class=" odd">private fun toNotifyUri(uri: Uri): Uri = DocumentsContract.buildDocumentUri(DOCUMENTS_PROVIDER_AUTHORITY, uri.toString())</li>
</ol><br />
<p>Vamos a aplicarlo. Podr&iacute;amos colocar un listener en nuestro queryChildDocuments, que se ocupa de actualizar la vista de los archivos si hay alg&uacute;n cambio, por ejemplo al renombrar o borrar un archivo. Antes de retornar el cursor, establecemos una uri de notificaci&oacute;n tal que as&iacute;:</p>
<div class="EnlighterJSWrapper classicEnlighterJSWrapper"><ol class="hoverEnabled classicEnlighterJS EnlighterJS">
<li class=" odd">resultCursor.setNotificationUri(context.contentResolver, toNotifyUri(Uri.parse(folderId)))</li>
</ol><br />
<p>De tal forma que cuando actualizamos un archivo, por ejemplo al renombrarlo, llamamos al content resolver y notificamos el cambio con la misma Uri que establecimos en el listener:</p>
<div class="EnlighterJSWrapper classicEnlighterJSWrapper"><ol class="hoverEnabled classicEnlighterJS EnlighterJS">
<li class=" odd">context.contentResolver.notifyChange(toNotifyUri(Uri.parse(folderId)), null)</li>
</ol><br />
<p>Si todo ha salido bien, el cambio se ver&aacute; reflejado al momento y el coste de esta actualizaci&oacute;n ser&aacute; m&iacute;nimo.</p>
<h4>Notificaci&oacute;n de errores</h4>
<p>La notificaci&oacute;n de errores se realiza mediante excepciones, lo cu&aacute;l no es muy agradable de tratar, pero es lo que hay. Uno de los puntos que tiene que mejorar el Document Provider.</p>
<p>Cuando al realizar una operaci&oacute;n, obtengamos un resultado no esperado, lanzamos una excepci&oacute;n y el Provider ya se encargar&aacute; de avisar al usuario de que la operaci&oacute;n no se complet&oacute; correctamente mediante un SnackBar.</p>
<h4>Que m&aacute;s funcionalidades nos ofrece el Document Provider</h4>
<p>Adem&aacute;s de todo lo comentado anteriormente, el document provider tiene m&aacute;s que ofrecer. Y es que s&oacute;lo hemos comentado lo m&aacute;s b&aacute;sico, pero todav&iacute;a podr&iacute;amos implementar las siguientes funcionalidades:</p>
<ul>
<li><strong>createDocument</strong>: Permite la creaci&oacute;n de ficheros o directorios dentro de nuestro Document Provider</li>
<li><strong>queryRecentDocuments</strong>: Permite mostrar los &uacute;ltimos archivos modificados en nuestro Document Provider</li>
<li><strong>openDocumentThumbnail</strong>: Permite mostrar miniaturas de nuestros documentos</li>
<li><strong>querySearchDocuments</strong>: Permite la b&uacute;squeda de archivos en nuestra aplicaci&oacute;n</li>
<li><strong>deleteDocument</strong>: Borrado de archivos</li>
<li><strong>renameDocument</strong>: Renombrado de archivos</li>
<li><strong>copyDocument</strong>: Copiar archivos dentro de nuestro document provider</li>
<li><strong>moveDocument</strong>: Mover archivos dentro de nuestro document provider</li>
</ul>
<p>Pero esto lo dejaremos para la pr&oacute;xima.</p>
<br>
<br>
<br>
<br>
<br>
<br>]]></content:encoded>
</item><item>
<title><![CDATA[¿Cómo estructurar tus proyectos en Go?]]></title>
<link>https://betabeers.com/blog/como-estructurar-tus-proyectos-go-400/</link>
<pubDate>Fri, 12 Apr 2019 08:47:17 +0000</pubDate>
<category>Colaboraciones</category>
<content:encoded><![CDATA[<p><em>Este art&iacute;culo ha sido escrito por <strong>Joan L&oacute;pez de la Franca</strong> y publicado originalmente en el blog de <a href="https://blog.friendsofgo.tech/posts/como_estructurar_tus_aplicaciones_go/">Friends of Go</a></em></p>
<p><strong><br />&iquest;Cu&aacute;ntas veces has empezado un nuevo proyecto en Go y te han surgido dudas sobre c&oacute;mo organizar t&uacute; c&oacute;digo?</strong> Por suerte, o por desgracia, la mayor&iacute;a de los que estamos metidos en esto venimos de entornos como Java, PHP o C#, d&oacute;nde el uso de <em>namespaces</em> est&aacute; extendido como una pr&aacute;ctica habitual. Sin embargo, cu&aacute;ndo uno empieza a programar en Go y descubre c&oacute;mo funciona su sistema de paquetes, tiende a quedarse bloqueado. Al fin y al cabo, no debemos olvidar que <strong>Go</strong> es un lenguaje que lleva la simplicidad al extremo.</p>
<p align="center"><img src="http://betabeers.com/uploads/blog/20190412_gopher_working_hard.png" alt="gopher working hard - Ashley McNamara" width="450px" /></p>
<h2 id="echandounvistazoalacomunidad">Echando un vistazo a la comunidad</h2>
<p>Por suerte, a d&iacute;a de hoy ya podemos considerar que la comunidad de Go es suficientemente grande como ser tomada como referencia. <a href="https://github.com/kubernetes/kubernetes">Kubernetes</a>, <a href="https://github.com/moby/moby">Moby</a>, <a href="https://github.com/cockroachdb/cockroach">CockroachDB</a>, <a href="https://github.com/gorilla/mux">Gorilla Mux</a>, <a href="https://github.com/hashicorp/packer">Packer</a>, entre otros, <strong>son proyectos con la madurez necesaria para ser tomados como ejemplo a la hora de estructurar nuestras aplicaciones.</strong> M&aacute;s all&aacute; de simples <em>Hola Mundo</em> o proyectos personales, estos tienen un gran bagaje en entornos de producci&oacute;n con altas cargas. Asi que, echemos un vistazo a ver qu&eacute; conclusiones podemos sacar de estos proyectos.</p>
<p>En primer lugar, si nos centramos en <strong>librer&iacute;as</strong>, pensadas para ser usadas como paquetes (las dependencias de toda la vida), como, por ejemplo, las t&iacute;picas de <a href="http://www.gorillatoolkit.org/">Gorilla</a> (<a href="https://github.com/gorilla/websocket">websocket</a>, <a href="https://github.com/gorilla/mux">mux</a>, etc), podemos ver que est&aacute;n <strong>estructuradas como un solo paquete</strong>. Lo que significa que tendremos nuestra librer&iacute;a desglosada en diferentes ficheros (que se corresponder&aacute;n con cada uno de los componentes de nuestra librer&iacute;a), pero todos estar&aacute;n en el directorio ra&iacute;z y formar&aacute;n parte del mismo paquete (package).</p>
<p>En segundo lugar, <strong>podemos ver como en proyectos m&aacute;s complejos, tipo aplicaciones,</strong> como lo es <a href="https://www.packer.io/">Packer</a> de <a href="https://www.hashicorp.com/">HashiCorp</a>, <strong>se sigue una estructura multinivel,</strong> similar a la que se sigue en otros lenguajes. De forma que, tenemos una primera estructura de directorios que representa cada uno de los componentes de nuestra aplicaci&oacute;n, y luego, en cada directorio, tenemos, o bien ya c&oacute;digo Go, o bien un segundo nivel de carpetas, que representa cada uno de los subcomponentes que componen el componente padre.</p>
<p>Los que ya lleven un tiempo pele&aacute;ndose con los paquetes de Go, <strong>seguramente pensar&aacute;n que esta opci&oacute;n no es muy aconsejable. Sin embargo, si recurrimos al <em>core</em> del lenguaje, nos encontramos con algo parecido,</strong> como lo son los paquetes agrupados. V&eacute;ase "net/http" o "crypto/md5". Sea como sea, no debemos olvidar que, cu&aacute;ndo procedamos a consumir dichos paquetes, el &uacute;nico nombre que figurar&aacute; en nuestro c&oacute;digo es el &uacute;ltimo de la cadena. Por ejemplo http.Client{}.</p>
<p>En tercer y &uacute;ltimo lugar, podemos observar otro patr&oacute;n habitual. <strong>El com&uacute;nmente denominado c&oacute;mo cmd + pkg.</strong> D&oacute;nde, todo el c&oacute;digo correspondiente a los diferentes componentes de nuestra aplicaci&oacute;n, ir&iacute;a dentro de la carpeta pkg, mientras que, el c&oacute;digo correspondiente a los puntos de entrada de nuestra aplicaci&oacute;n (CLI, API, etc) ir&iacute;a en el directorio cmd. Incluso se puede llegar a ver una combinaci&oacute;n de las dos &uacute;ltimas.</p>
<h2 id="reflexinqutipodeproyectoestsconstruyendo">Reflexi&oacute;n: &iquest;Qu&eacute; tipo de proyecto est&aacute;s construyendo?</h2>
<p>Despu&eacute;s de echar un vistazo por algunos de los proyectos escritos en Go m&aacute;s populares, nos podemos dar cuenta que no hemos sacado mucho en claro. Sin embargo, parece que s&iacute; hemos llegado a una conclusi&oacute;n. Antes de plantearnos c&oacute;mo estructurar nuestras aplicaciones, debemos pensar bien la respuesta a la siguiente pregunta:</p>
<p><strong>- &iquest;Qu&eacute; tipo de proyecto vamos a construir?</strong></p>
<p>Ante esta cuesti&oacute;n, tenemos varias posibles respuestas. Alguna de ellas ya la hemos anticipado anteriormente:</p>
<ul>
<li>
<p>&iquest;Quiz&aacute;s una librer&iacute;a que funcionar&aacute; por si sola y ser&aacute; usada como dependencia en otras aplicaciones?</p>
<p>V&eacute;ase una librer&iacute;a para la gesti&oacute;n de protocol buffers.</p>
</li>
<li>
<p>&iquest;Quiz&aacute;s una aplicaci&oacute;n que funcionar&aacute; estrictamente como tal, sin tener funcionalidades exportadas como librer&iacute;as?</p>
<p>V&eacute;ase una aplicaci&oacute;n que expondr&aacute; una API REST de nuestro negocio.</p>
</li>
<li>
<p>&iquest;Quiz&aacute;s una aplicaci&oacute;n, que adem&aacute;s tambi&eacute;n tendr&aacute; funcionalidades expuestas como paquetes?</p>
<p>V&eacute;ase una aplicaci&oacute;n CLI de transformaci&oacute;n de im&aacute;genes que tambi&eacute;n se podr&aacute; usar como librer&iacute;a en otros desarrollos.</p>
</li>
</ul>
<p>Como vemos, las posibles respuestas a dicha pregunta, son varias, sin embargo, parece que <strong>la mayor duda surge cu&aacute;ndo estamos dispuestos a construir una aplicaci&oacute;n grande y compleja,</strong> no cu&aacute;ndo vamos a desarrollar una "simple" librer&iacute;a. Pues, en ese &uacute;ltimo caso, como ya adelantemos anteriormente, lo que haremos ser&aacute; tener un &uacute;nico paquete con varios ficheros.</p>
<h2 id="cmoestructurartusaplicacionesengo">&iquest;C&oacute;mo estructurar tus <strong>aplicaciones</strong> en Go?</h2>
<p>Como hemos visto, parece que, a pesar de que hay decisiones ya estandarizadas, a&uacute;n no hay ning&uacute;n enfoque que se haya convertido en el referente absoluto. Es por eso que, <strong>desde <a href="https://friendsofgo.tech">Friends of Go</a>, queremos hacer nuestra propuesta sobre como estructurar vuestras aplicaciones en Go.</strong> Por su puesto, &eacute;sta no es m&aacute;s que una confluencia de varias de las estrategias que ya se siguen hoy d&iacute;a en la comunidad. Ya tenemos suficiente con mojarnos y dar nuestra opini&oacute;n al respecto, solo nos faltaba decir que, adem&aacute;s, la hemos inventado nosotros, lo cu&aacute;l no ser&iacute;a verdad.</p>
<p>Entonces, &iquest;qu&eacute; proponemos? Veamos!</p>
<pre><code>your-application-name
├── cmd
|   ├── your-application-name-cli
|   |    └── main.go
|   ├── your-application-name-api
|   |    └── main.go
├── internal/
|   ├── package01
|   |    ├── file01.go
|   |    └── file02.go
|   ├── package02
|   |    ├── file01.go
|   |    └── file02.go
├── pkg/
|   ├── library01
|   |    ├── file01.go
|   |    └── file02.go
|   ├── library02
|   |    ├── file01.go
|   |    └── file02.go
├── vendor/
├── LICENSE
└── README.md
</code></pre>
<p>D&oacute;nde cada carpeta (o estructura de carpetas), tendr&iacute;a su finalidad:</p>
<ul>
<li>
<p><strong>/cmd</strong>, ser&aacute; la carpeta que va a contener todos los puntos de entrada de nuestra aplicaci&oacute;n. Cada uno de ellos, ser&aacute; un paquete de la carpeta cmd y cada uno tendr&aacute; su main.go adem&aacute;s del resto de c&oacute;digo necesario (configuraciones, entornos, inyecci&oacute;n de dependencias, etc). Es importante recordar que, los paquetes dentro de cmd se corresponder&aacute;n con el nombre de los puntos de entrada, es decir, con el nombre de los binarios de las aplicaciones correspondientes.</p>
</li>
<li>
<p><strong>/internal</strong>, ser&aacute; la carpeta que va a contener todos los paquetes que representan los componentes de nuestra aplicaci&oacute;n, que no ser&aacute;n expuestos y que no van a tener por qu&eacute; funcionar fuera de ella.</p>
</li>
<li>
<p><strong>/pkg</strong>, ser&aacute; la carpeta que va a contener todos aquellos componentes de nuestra aplicaci&oacute;n que s&iacute; queremos exponer, y que van a tener que poder funcionar fuera de ella (reutilizaci&oacute;n).</p>
</li>
<li>
<p><strong>/vendor</strong>, (opcional) ser&aacute; la carpeta que va a contener todas las dependencias de terceros en caso de que a&uacute;n no hayamos <a href="../../posts/migrar-a-go-modules-facilmente/">migrado a Go Modules</a> o que a&uacute;n sigamos una estrategia de <em>vendoring</em>.</p>
</li>
</ul>
<p>Como podemos ver, &eacute;sta estrategia nos permite cubrir los distintos casos planteados anteriormente, pero no solo eso, sin&oacute; que adem&aacute;s, estamos indicando, con nuestra estructura de carpetas, qu&eacute; paquetes van a ser expuestos y cu&aacute;les no, de forma f&aacute;cil e intuitiva. Sobretodo, si tenemos en cuenta que los paquetes dentro del directorio internal directamente no van a poder ser importados desde fuera de nuestro proyecto (por defecto).</p>
<h2 id="kitelsharedkernelmsgopher">Kit, el <em>Shared Kernel</em> m&aacute;s <em>gopher</em></h2>
<p>Cu&aacute;ndo hablamos de <a href="https://es.wikipedia.org/wiki/Dise%C3%B1o_guiado_por_el_dominio">Domain-Driven Design (DDD)</a>, es habitual hablar del concepto <em>Shared Kernel</em>, el cu&aacute;l hace referencia a todo el conjunto de tipos de datos y funciones asociadas que son compartidas por los diferentes contextos de nuestro dominio.</p>
<p>En la comunidad Go, los compa&ntilde;eros de <a href="https://www.ardanlabs.com/">ArdanLabs</a> proponen lo que ellos han llamado <strong>Kit</strong>. Concepto con el que queremos cerrar &eacute;ste art&iacute;culo.</p>
<p>Seg&uacute;n su definici&oacute;n, <strong>Kit</strong> deber&iacute;a ser el proyecto que representa la biblioteca est&aacute;ndar de tu organizaci&oacute;n, y &eacute;sta deber&iacute;a ser &uacute;nica. Es por eso, que los paquetes que formen parte de la misma, deben dise&ntilde;arse teniendo especial cuidado con la portabilidad de los mismos. Los paquetes dentro de Kit deber&iacute;an poder ser utilizados en m&uacute;ltiples aplicaciones y proporcionar un dominio de funcionalidad espec&iacute;fico. <strong>Recordad, menos utils, common y shared y m&aacute;s time, strings y crypto.</strong></p>]]></content:encoded>
</item><item>
<title><![CDATA[Architecture Components: Paging Library (Parte I) #AndroidMeetsKotlin]]></title>
<link>https://betabeers.com/blog/architecture-components-paging-library-parte-i-androidmeetskotlin-399/</link>
<pubDate>Thu, 04 Apr 2019 23:27:37 +0000</pubDate>
<category>Colaboraciones</category>
<content:encoded><![CDATA[<h1 id="h.wrv53akk8rq2" class="c13"><img style="display: block; margin-left: auto; margin-right: auto;" src="http://betabeers.com/uploads/blog/20190404_image_1.jpg" alt="" width="500" height="275" /></h1>
<p class="c1">Siguiendo la l&iacute;nea de <a class="c4" href="https://www.google.com/url?q=https://developer.android.com/topic/libraries/architecture&amp;sa=D&amp;ust=1554416169709000">Architecture Components</a>, ahora vas a implementar una librer&iacute;a poco conocida llamada <a class="c4" href="https://www.google.com/url?q=https://developer.android.com/topic/libraries/architecture/paging&amp;sa=D&amp;ust=1554416169710000">Paging Library</a>. La implementaci&oacute;n que vas a realizar consistir&aacute; en a&ntilde;adir a la parte de tu DataSource / Repository la capacidad de paginaci&oacute;n. Puesto que el contenido es muy amplio, en esta secci&oacute;n ver&aacute;s los cambios que debes aplicar en build.gradle para a&ntilde;adir la dependencia, DAO, GithubRepoLocalDataSource y creaci&oacute;n del GithubRepoBoundaryCallback. En el siguiente post terminaremos su integraci&oacute;n, no obstante, si est&aacute;s ansioso por verla al completo, tienes todo el c&oacute;digo disponible en el siguiente <a class="c4" href="https://www.google.com/url?q=https://github.com/franlopjur/ArchitectureComponentsSample/tree/paging&amp;sa=D&amp;ust=1554416169710000">enlace</a> bajo la rama &ldquo;paging&rdquo;. Sin m&aacute;s, &iexcl;Manos a la obra!</p>
<p class="c1 c11"> </p>
<h2 id="h.mbjgp0h3ul7d" class="c10">Objetivo</h2>
<p class="c1">El principal objetivo de esta librer&iacute;a es facilitar la carga paginada de datos en un listado. Anteriormente, siempre has tenido que usar una librer&iacute;a de terceros o incluso tu propia implementaci&oacute;n, la cual pod&iacute;a contener fallos y no acoplarse perfectamente a la arquitectura planteada por Google. Gracias a Paging Library, el acople es perfecto y sigue todos los est&aacute;ndares marcados por la compa&ntilde;&iacute;a.</p>
<p class="c1 c11"> </p>
<h2 id="h.x5ma9gejcmre" class="c10">Nuevos componentes</h2>
<p class="c1">Los principales componentes que te vas a encontrar son:</p>
<ul class="c5 lst-kix_gw3ql5k6362h-0 start">
<li class="c1 c7"><a class="c4" href="https://www.google.com/url?q=https://developer.android.com/reference/android/arch/paging/PagedList.html&amp;sa=D&amp;ust=1554416169712000">PagidList</a>. Simplificando, se trata de una colecci&oacute;n que carga datos paginados as&iacute;ncronamente. Puede usarse para cargar datos desde los DataSources que definas.</li>
<li class="c1 c7"><a class="c4" href="https://www.google.com/url?q=https://developer.android.com/reference/android/arch/paging/LivePagedListBuilder.html&amp;sa=D&amp;ust=1554416169713000">LivePagedListBuilder</a>. Crea un LiveData basado en <a class="c4" href="https://www.google.com/url?q=https://developer.android.com/reference/android/arch/paging/DataSource.Factory&amp;sa=D&amp;ust=1554416169714000">DataSource.Factory</a> y una configuraci&oacute;n mediante <a class="c4" href="https://www.google.com/url?q=https://developer.android.com/reference/android/arch/paging/PagedList.Config&amp;sa=D&amp;ust=1554416169714000">PagedList.Config</a>.</li>
<li class="c1 c7"><a class="c4" href="https://www.google.com/url?q=https://developer.android.com/reference/android/arch/paging/PagedList.BoundaryCallback.html&amp;sa=D&amp;ust=1554416169715000">BoundaryCallback</a>. Marca cuando un <a class="c4" href="https://www.google.com/url?q=https://developer.android.com/reference/android/arch/paging/PagedList&amp;sa=D&amp;ust=1554416169715000">PagedList</a> ha llegado al final de sus datos disponibles.</li>
<li class="c1 c7"><a class="c4" href="https://www.google.com/url?q=https://developer.android.com/reference/android/arch/paging/PagedListAdapter.html&amp;sa=D&amp;ust=1554416169715000">PagidListAdapter</a>. Basado en RecyclerView.Adapter, presenta los datos paginados en un RecyclerView. Adem&aacute;s, tiene los distintos callbacks de PagedList para saber cuando los datos son cargados y utiliza <a class="c4" href="https://www.google.com/url?q=https://developer.android.com/reference/android/support/v7/util/DiffUtil&amp;sa=D&amp;ust=1554416169716000">DiffUtil</a> para calcular los nuevos datos cargados.</li>
</ul>
<p class="c1 c11"> </p>
<h2 id="h.e9lcogktq5h" class="c10">Aplicando cambios</h2>
<p class="c1">Continuar&aacute;s el proyecto que has ido creando siguiendo los anteriores posts <a class="c4" href="https://www.google.com/url?q=https://betabeers.com/blog/architecture-components-repository-datasources-androidmeetskotlin-395/&amp;sa=D&amp;ust=1554416169717000">(Architecture Components: Repository y DataSources #AndroidMeetsKotlin de Fran L&oacute;pez</a>), pero en este caso debes utilizar la rama &ldquo;paging&rdquo; para separar las distintas versiones. Los cambios que debes hacer son:</p>
<h3 id="h.up8pw3thujz6" class="c0">1.- A&ntilde;ade la dependencia en build.gradle</h3>
<p class="c1">Tienes que a&ntilde;adir la siguiente dependencia, en tu caso &ldquo;pagingVersion&rdquo; es igual a &ldquo;1.0.1&rdquo;.</p>
<p class="c1 c11"><img src="http://betabeers.com/uploads/blog/20190404_image_2.png" alt="" width="500" height="30" /></p>
<h3 id="h.oqyc021azz8z" class="c0">2.- Cambios en el DAO</h3>
<p class="c1">El m&eacute;todo &ldquo;getReposAsync&rdquo; ya no retornar&aacute; un LiveData&lt;List&gt; sino un DataSource.Factory&lt;Int, GithubRepoDomain&gt;. De esta forma, el Int ser&aacute; la clave y tendr&aacute; la informaci&oacute;n de la posici&oacute;n en el PagedList (<a class="c4" href="https://www.google.com/url?q=https://developer.android.com/reference/android/arch/paging/PositionalDataSource&amp;sa=D&amp;ust=1554416169719000">PositionalDataSource</a>).</p>
<p class="c1">Tambi&eacute;n debes a&ntilde;adir un m&eacute;todo que te retornar&aacute; la lista de repositorios de forma directa, esto lo usar&aacute;s para saber la p&aacute;gina que has consultado en una consulta anterior en base a los datos ya guardados en base de datos. Este m&eacute;todo quedar&aacute; de la siguiente forma:</p>
<p class="c1 c11"> </p>
<p class="c1"><img src="http://betabeers.com/uploads/blog/20190404_image_3.png" alt="" width="600" height="40" /></p>
<p class="c1 c11"> </p>
<p class="c1">La implementaci&oacute;n la puedes encontrar en el siguiente <a class="c4" href="https://www.google.com/url?q=https://github.com/franlopjur/ArchitectureComponentsSample/blob/paging/app/src/main/java/com/betabeers/architecturecomponentsexample/db/dao/GithubRepoDao.kt&amp;sa=D&amp;ust=1554416169720000">enlace</a>.</p>
<h3 id="h.xlyjqolkbpex" class="c0">3.- Cambios en el DataSource</h3>
<p class="c1">El cambio anterior obliga a modificar la implementaci&oacute;n de la clase GithubRepoLocalDataSource, concretamente el m&eacute;todo &ldquo;getGithubRepos&rdquo;, &eacute;ste debe retornar tambi&eacute;n DataSource.Factory&lt;Int, GithubRepoDomain&gt;. Adem&aacute;s, tendr&aacute; un m&eacute;todo adicional para obtener los datos directamente sin la necesidad de un LiveData que usar&aacute;s m&aacute;s adelante.</p>
<p class="c1 c11"> </p>
<p class="c1"><img src="http://betabeers.com/uploads/blog/20190404_image_4.png" alt="" width="600" height="94" /></p>
<p class="c1 c11"> </p>
<p class="c1">La implementaci&oacute;n la puedes encontrar en el siguiente <a class="c4" href="https://www.google.com/url?q=https://github.com/franlopjur/ArchitectureComponentsSample/blob/paging/app/src/main/java/com/betabeers/architecturecomponentsexample/data/GithubRepoLocalDataSource.kt&amp;sa=D&amp;ust=1554416169722000">enlace</a>.</p>
<h3 id="h.4cu32a8g9s5g" class="c0">4.- Crear el GithubRepoBoundaryCallback</h3>
<p class="c1">Su objetivo es el de calcular cu&aacute;ndo se ha llegado al final de los datos disponibles. En el momento que un DataSource se queda sin datos para cargar, se llamar&aacute; al m&eacute;todo &ldquo;onItemAtEndLoaded(Object)&rdquo;. En este caso, debes llamar a GithubRepoApiDataSource para que empiece la carga de datos desde los servicios Web. Una vez que los datos lleguen de forma satisfactoria, se guardar&aacute;n en BD y, debido a que est&aacute; observando la BD con el LiveData, provocar&aacute; que la UI se actualice de inmediato con los datos nuevos.</p>
<p class="c1">Es importante marcar cuando una llamada as&iacute;ncrona ya est&aacute; en curso para que no hagas una nueva. En cuanto a sus m&eacute;todos, s&oacute;lo tiene tres m&eacute;todos:</p>
<ul class="c5 lst-kix_lr7od2o89hf7-0 start">
<li class="c1 c7"><strong>onItemAtEndLoaded(T itemAtEnd)</strong>. Se llama cuando el elemento al final de PagedList se ha cargado y el acceso se ha producido dentro de la distancia de precarga (&ldquo;prefetchDistance&rdquo;) establecido en su creaci&oacute;n.</li>
<li class="c1 c7"><strong>onZeroItemsLoaded()</strong>. Se llama cuando se devuelven cero elementos desde una carga inicial de la fuente de datos de PagedList.</li>
<li class="c1 c7"><strong>onItemAtFrontLoaded(T itemAtFront)</strong>. Lo mismo que &ldquo;onItemAtEndLoaded&rdquo; pero para el caso del primer elemento.</li>
</ul>
<p class="c1 c11"> </p>
<p class="c1">Para este ejemplo, necesitas que tu BoundaryCallback, al que llamar&aacute;s &ldquo;GithubRepoBoundaryCallback&rdquo;, tenga los siguientes par&aacute;metros:</p>
<ul class="c5 lst-kix_ofw3db1tajm8-0 start">
<li class="c1 c7"><strong>query</strong>. Se trata de la consulta a realizar.</li>
<li class="c1 c7"><strong>githubRepoApiDataSource</strong>. Lo utilizar&aacute;s para cargar los siguientes datos.</li>
<li class="c1 c7"><strong>githubRepoLocalDataSource</strong>. Lo usar&aacute;s para almacenar los datos en BD cuando sean retornados del servicio.</li>
<li class="c1 c7"><strong>isRequesting</strong>. Controla si hay una llamada en curso. Este flag anteriormente se encontraba en el Repository, por lo que debes borrarlo en &eacute;l.</li>
<li class="c1 c7"><strong>lastPageRequestedNumber</strong>. Tiene la informaci&oacute;n de la &uacute;ltima p&aacute;gina consultada al servicio.</li>
</ul>
<p class="c1 c11"> </p>
<p class="c1">La implementaci&oacute;n que har&aacute;s al sobreescribir los m&eacute;todos &ldquo;onZeroItemsLoaded&rdquo; y &ldquo;onItemAtEndLoaded&rdquo; debe ser la de llamar al servicio para obtener la informaci&oacute;n necesaria. &Eacute;stos quedar&iacute;an tal que as&iacute;:</p>
<p class="c1"><img src="http://betabeers.com/uploads/blog/20190404_image_5.png" alt="" width="500" height="136" /></p>
<p class="c1 c11"> </p>
<p class="c1">Para tu caso, no necesitar&aacute;s sobreescribir onItemAtFrontLoaded.</p>
<p class="c1 c11"> </p>
<p class="c1">En cuanto al m&eacute;todo &ldquo;requestNewDataAndSave(query: String)&rdquo;, debe marcar el boolean a true al inicio del mismo y realizar&aacute; la llamada pas&aacute;ndole la consulta y la p&aacute;gina. Una vez que hemos obtenido una respuesta correcta, inserta los datos a la BD e incrementa el n&uacute;mero de p&aacute;gina. Tanto si la llamada ha ido bien como si ha ido mal, marcaremos el flag de consulta a false. El c&oacute;digo quedar&iacute;a tal que as&iacute;:</p>
<p class="c1 c11"> </p>
<p class="c1"><img src="http://betabeers.com/uploads/blog/20190404_image_6.png" alt="" width="400" height="218" /></p>
<p class="c1 c11"> </p>
<p class="c1">La implementaci&oacute;n la puedes encontrar en el siguiente <a class="c4" href="https://www.google.com/url?q=https://github.com/franlopjur/ArchitectureComponentsSample/blob/paging/app/src/main/java/com/betabeers/architecturecomponentsexample/data/GithubRepoBoundaryCallback.kt&amp;sa=D&amp;ust=1554416169727000">enlace</a>.</p>
<p class="c1 c11"> </p>
<h2 id="h.cpaja6rfy0om" class="c10">Hasta aqu&iacute; por ahora</h2>
<p class="c1">Resumiendo, despu&eacute;s de todo lo anterior, tienes una base s&oacute;lida para poder finalizar la implementaci&oacute;n en el siguiente post. Como has podido ver, los cambios no son complejos y permiten no perderte por una documentaci&oacute;n extensa.</p>
<p class="c1 c11"> </p>]]></content:encoded>
</item><item>
<title><![CDATA[Crear una página de opciones con ACF en WordPress]]></title>
<link>https://betabeers.com/blog/crear-una-pagina-opciones-acf-wordpress-398/</link>
<pubDate>Fri, 29 Mar 2019 08:33:12 +0000</pubDate>
<category>Colaboraciones</category>
<content:encoded><![CDATA[<p>Esta semana vamos a centrarnos en la creaci&oacute;n de una soluci&oacute;n que deber&iacute;a facilitarnos el trabajo como desarrolladores, pero tambi&eacute;n resultar <strong>de utilidad para las personas dentro de un proyecto que se dediquen a gestionar los contenidos</strong> y el mantenimiento de un sitio web.</p>
<p>Partamos de un supuesto en el cual estamos trabajando en un proyecto y nos encontramos con multitud de valores a completar que, siendo diferentes para cada cliente, responden a patrones pr&aacute;cticamente id&eacute;nticos para todos los casos.</p>
<p> </p>
<p><img style="display: block; margin-left: auto; margin-right: auto;" title="P&aacute;gina Opciones ACF WordPress" src="http://betabeers.com/uploads/blog/20190329_pagina-opciones-acf-wordpress.jpg" alt="P&aacute;gina Opciones ACF WordPress" width="400" height="267" /></p>
<p>Me refiero en este caso a <strong>datos del cliente</strong>, que muy probablemente aparezcan, incluso d<strong>e manera repetida a lo largo de todo el contenido</strong> y secciones de la web: nombre de la empresa, CIF, direcci&oacute;n, c&oacute;digo postal, localidad, provincia, tel&eacute;fono de contacto, email, perfiles en redes sociales.</p>
<p>Si alguno de estos datos cambia con el tiempo, deberemos recordar donde se encontraba ese dato y, en ocasiones, trabajar directamente con c&oacute;digo para actualizar esa informaci&oacute;n. Este proceso en cada sitio web y de cada cliente con la correspondiente inversi&oacute;n de tiempo realizada en el transcurso de la actualizaci&oacute;n.</p>
<p>En este art&iacute;culo descubriremos c<strong>&oacute;mo podemos gestionar la informaci&oacute;n de manera centralizada</strong> y replicar esta personalizaci&oacute;n en cada uno de nuestros proyectos. Para ello necesitaremos:</p>
<ul>
<li>Crear una p&aacute;gina de opciones para nuestro cliente</li>
<li>Definir los datos que podr&iacute;a ser necesario modificar</li>
<li>Imprimir los valores en el frontend con funciones</li>
<li>Imprimir los valores en el editor de contenidos mediante shortcodes</li>
</ul>
<p>Todo el proceso detallado a continuaci&oacute;n puede ir incluido en el archivo de funciones <em>functions.php</em> de nuestro tema activo o encapsulado en forma de plugin personalizado.</p>
<h2>Creando una p&aacute;gina de opciones con Advanced Custom Fields</h2>
<p>En el caso que nos ocupa vamos a crear una p&aacute;gina de opciones dentro del men&uacute; de administraci&oacute;n de WordPress mediante la versi&oacute;n premium del plugin <a href="https://www.advancedcustomfields.com/" target="_blank">ACF Advanced Custom Fields</a>.</p>
<p>La funci&oacute;n de ACF que necesitaremos para crear la p&aacute;gina de opciones es la siguiente:</p>
<pre>if( function_exists('acf_add_options_page') ) {
acf_add_options_page(array(
'page_title' =&gt; 'Opciones para Cliente',
'menu_title' =&gt; 'Opciones Cliente',
'menu_slug' =&gt; 'opciones-cliente',
'capability' =&gt; 'edit_posts',
'redirect' =&gt; false,
'position'    =&gt; '80',
'icon_url'    =&gt; 'dashicons-info',
));
}
</pre>
<p>Los par&aacute;metros que podemos utilizar est&aacute;n correctamente detallados en la <a href="https://www.advancedcustomfields.com/resources/acf_add_options_page/" target="_blank">documentaci&oacute;n de ACF</a> aunque explicaremos brevemente a continuaci&oacute;n los que hemos usado para este ejemplo.</p>
<ul>
<li><strong>page_title</strong>. El t&iacute;tulo de la p&aacute;gina de opciones</li>
<li><strong>menu_title</strong>.El t&iacute;otulo que se mostrar&aacute; en el men&uacute; del panel de administraci&oacute;n</li>
<li><strong>menu_slug</strong>. Enlace de la p&aacute;gina de opciones</li>
<li><strong>capability</strong>. Posibilidad de que el rol de editor pueda ver esta secci&oacute;n</li>
<li><strong>position</strong>. Posici&oacute;n dentro del men&uacute; del administrador</li>
<li><strong>icon_url</strong>. El icono que queremos utilizar en el men&uacute;</li>
</ul>
<h2>Grupos de campos para la p&aacute;gina de opciones</h2>
<p>Una vez que tenemos nuestra secci&oacute;n de opciones <strong>es hora de completarla con los campos que vayamos a querer definir</strong>. Es importante en este caso que tengamos en cuenta cu&aacute;les van a ser los valores que m&aacute;s vamos a reutilizar en nuestros proyectos y que son susceptibles de ser actualizados con m&aacute;s frecuencia.</p>
<p>Para este caso crearemos los campos de nombre de empresa, persona de contacto, tel&eacute;fono y correo electr&oacute;nico, aunque como os pod&eacute;is imaginar, la casu&iacute;stica es muy diversa.</p>
<ul>
<li>Accedemos al men&uacute; de Campos Personalizados de ACF</li>
<li>Creamos un grupo de campos nuevo por ejemplo con el nombre de Cliente</li>
<li>A&ntilde;adimos los campos de ACF que necesitaremos: empresa, nombre, email y tel&eacute;fono</li>
</ul>
<p><img src="../../static/uploads/blog/20190329_campos-cliente.png" alt="campos-cliente" width="600" height="320" /></p>
<ul>
<li>Vinculamos este grupo de campos con la p&aacute;gina de opciones previamente creada</li>
</ul>
<p><img src="../../static/uploads/blog/20190329_campos-pagina-opciones.png" alt="campos-pagina-opciones" width="600" height="250" /></p>
<p>De este modo podremos comprobar c&oacute;mo nuestra p&aacute;gina de opciones creada con antelaci&oacute;n va cogiendo forma y a&ntilde;ade los campos previamente indicadod.</p>
<p><img src="../../static/uploads/blog/20190329_campos-opciones.png" alt="pagina-opciones" width="600" height="321" /></p>
<h2>Funciones para imprimir los campos en el sitio web</h2>
<p>Mediante funciones nativas de Advanced Custom Fields vamos a poder imprimir esta informaci&oacute;n en el lugar de nuestro tema o plugin que queramos. Deberemos tener cuidado a la hora de hacer indicar que los valores vienen dados desde una p&aacute;gina de opciones a&ntilde;adiendo option u options.</p>
<pre>the_field('empresa', 'options'); 
the_field('nombre, 'options'); 
the_field('telefono, 'options'); 
the_field('email, 'options'); 
</pre>
<p>Si necesitamos ayuda con la implementaci&oacute;n podemos acudir directamente a la documentaci&oacute;n de ACF ( <a href="https://www.advancedcustomfields.com/resources/">https://www.advancedcustomfields.com/resources/</a> ) que nos permitir&aacute; conocer todas las opciones disponibles.</p>
<h2>Creando Shortcodes para insertar en el editor de contenido</h2>
<p>Hasta aqu&iacute; estamos utilizando herramientas que facilitan la creaci&oacute;n de campos dentro de WordPress, pero es un proceso b&aacute;sicamente t&eacute;cnico. En el siguiente paso de este tutorial vamos a conseguir que estos campos puedan ser mostrados en cualquier emplazamiento que permita el uso de shortcodes.</p>
<p>Podemos utilizar shortcodes si lo que necesitamos es a&ntilde;adirlo directamente dentro del contenido de un post o en un espacio de widgets de nuestro proyecto.</p>
<p>En este caso, contamos tambi&eacute;n con la ayuda de ACF que permite incorporar sus propios shortcodes ( <a href="https://www.advancedcustomfields.com/resources/shortcode/">https://www.advancedcustomfields.com/resources/shortcode/</a> ).</p>
<p>En nuestro caso podremos utilizar los siguientes shortcodes all&aacute; donde lo creamos conveniente.</p>
<pre>[acf field="nombre" post_id="options"]
[acf field="empresa" post_id="options"]
[acf field="email" post_id="options"]
[acf field="telefono" post_id="options"]
</pre>
<h2>Shortcodes personalizados</h2>
<p>Si adem&aacute;s queremos darle un toque interesante de personalizaci&oacute;n y que sem&aacute;nticamente el <em>shortcode</em> sea mucho m&aacute;s entendible podemos crear nuestros propios <strong>shortcodes personalizados</strong> para cada uno de los valores.</p>
<pre>function create_shortcode_empresa() {
return get_field(&lsquo;empresa&rsquo;, &lsquo;options&rsquo;);
}
add_shortcode(&lsquo;empresa&rsquo;, &lsquo;shortcode_empresa&rsquo;);
</pre>
<p>El shortcode resultante es mucho m&aacute;s sencillo de entender y de implementar. Tendr&aacute; un formato como el que sigue:</p>
<pre>[empresa]
</pre>
<h2>Conclusiones</h2>
<p>En muchas ocasiones hemos buscado soluciones a nivel personal que nos ayudan a mejorar nuestro trabajo como desarrolladores, bien porque conseguimos agilizar los resultados o bien porque nos permiten incluir herramientas y software mejores.</p>
<p>Las optimizaciones no solo deben ir encaminadas a mejorar nuestro propio trabajo sino que tambi&eacute;n podemos hacer la vida m&aacute;s f&aacute;cil a nuestros clientes a los compa&ntilde;eros de equipo encargados de la edici&oacute;n y creaci&oacute;n de contenidos.</p>
<p>As&iacute; que, tan solo un consejo para terminar. Piensa qu&eacute; tareas llevan m&aacute;s tiempo en un equipo de trabajo y crea procesos que hagan m&aacute;s f&aacute;cil la vida de los dem&aacute;s.</p>]]></content:encoded>
</item><item>
<title><![CDATA[Flutter: primer contacto]]></title>
<link>https://betabeers.com/blog/flutter-primer-contacto-397/</link>
<pubDate>Fri, 22 Mar 2019 11:08:22 +0000</pubDate>
<category>Colaboraciones</category>
<content:encoded><![CDATA[<p><em>Este art&iacute;culo ha sido escrito por <a href="https://twitter.com/David_A_Velasco?lang=es" target="_blank">David A. Velasco</a> y publicado originalmente en el blog de <a href="https://solidgeargroup.com/flutter-primer-contacto?lang=es" target="_blank">Solid GEAR</a>.</em></p>
<p>Flutter est&aacute; en todas partes. El pasado mes de diciembre <a href="https://developers.googleblog.com/2018/12/flutter-10-googles-portable-ui-toolkit.html" rel="noreferrer noopener" target="_blank">fue publicada la versi&oacute;n 1.0</a>, declar&aacute;ndolo as&iacute; como listo para producci&oacute;n. El anuncio vino acompa&ntilde;ado de un colorido inventario de organizaciones que ya han desarrollado sus apps con Flutter, incluyendo nombres llamativos como Alibaba o Tencent. Aparecen comunidades de desarrolladores Flutter como setas. Es nuevo, es brillante, es cool. Es de Google.</p>
<p> </p>
<p>&iquest;Pero qu&eacute; diantres es realmente Flutter?</p>
<h2>Buenos d&iacute;as, princesa</h2>
<p><img style="float: right;" src="https://solidgeargroup.com/wp-content/uploads/2019/03/02_happy_fluttering.jpg" alt="Flutter and happiness" width="320" height="480" /></p>
<p>Flutter es un paquete de desarrollo de software (SDK) para crear <strong>interfaces de usuario multiplataforma</strong> en iOS y Android. Es un proyecto de c&oacute;digo abierto desarrollado por Google y una comunidad de voluntarios.</p>
<div class="wp-block-image">
<p>Flutter permite construir aplicaciones m&oacute;viles completas dando acceso a los sistemas de conectividad, almacenamiento y sensores del dispositivo en que se ejecutan, pero su raz&oacute;n de existir es la interfaz de usuario. Flutter capacita a los desarrolladores para crear interfaces donde prima el <strong>dise&ntilde;o de marca</strong>, manteniendo una &uacute;nica base de c&oacute;digo compatible con iOS y Android. Al mismo tiempo, permite adherirse al estilo de la plataforma cuando as&iacute; se desea, proporcionando dos conjuntos de componentes (widgets) separados, <strong>Material Design y Cupertino</strong>.</p>
<p>La idea de hacer apps multiplataforma tiene poco de original. Mantener una sola base de c&oacute;digo para iOS y Android es terriblemente atractivo como forma de reducir costes de desarrollo y mantenimiento, al menos en teor&iacute;a.</p>
<p>La aproximaci&oacute;n de Flutter es similar a la de muchos motores de videojuegos. El c&oacute;digo de la aplicaci&oacute;n, junto con el del propio Flutter, se compila a una biblioteca <strong>nativa</strong>. &Eacute;sta se empaqueta en un fichero APK o IPA junto con un proyecto base que act&uacute;a como &ldquo;ejecutor&rdquo; de la biblioteca. Cuando la app se lanza, el proyecto base carga la biblioteca y delega en ella cualquier evento de usuario o de sistema que reciba, as&iacute; como la responsabilidad de dibujar la pantalla.</p>
<p>El punto diferenciador con respecto a otras soluciones multiplataforma es que el SDK no se apoya ni en tecnolog&iacute;a web como Cordova, ni en los componentes gr&aacute;ficos de la plataforma base como <a href="https://solidgeargroup.com/desarrollo-multiplataforma-con-xamarin?lang=es" rel="noreferrer noopener" target="_blank">Xamarin</a>. <strong>Flutter incluye un motor de renderizado gr&aacute;fico propio y sus widgets se ejecutan directamente sobre &eacute;l</strong>. El motor est&aacute; basado en Skia, una biblioteca de dibujo en 2D que forma parte de la pila de Chrome, Firefox o el propio Android, y se incluye en cada aplicaci&oacute;n Flutter compilado de forma nativa.</p>
<p>Con esta implementaci&oacute;n Flutter apuesta por una pila tecnol&oacute;gica m&aacute;s independiente del sistema base, m&aacute;s delgada, y con m&aacute;s margen para optimizar tanto el rendimiento de las interfaces como su fidelidad al dise&ntilde;o original.</p>
<br />
<h2>Mi tesoro</h2>
<p>Adem&aacute;s del SDK, Flutter proporciona una serie de <strong>herramientas</strong> necesarias para la creaci&oacute;n de aplicaciones. Todas ellas son accesibles bien desde l&iacute;nea de comandos, bien desde las respectivas extensiones oficiales para Android Studio/IntelliJ IDEA y Visual Studio Code.</p>
<p>La estrella del paquete es, sin duda, el <strong>sistema de recarga en caliente</strong>. &Eacute;ste permite que casi cualquier cambio hecho al c&oacute;digo se refleje inmediatamente en ejecuci&oacute;n con s&oacute;lo guardar fichero. Funciona sorprendentemente bien, tanto en dispositivos reales como en emuladores, en iOS y en Android.</p>
<p>Lo complementa estupendamente el <strong>inspector de widgets</strong>, un panel en las extensiones de los IDEs capaz de localizar y observar en tiempo real el estado de cada elemento de la interfaz. La combinaci&oacute;n de ambos consigue que no se eche en falta ninguna herramienta de edici&oacute;n gr&aacute;fica, delegando su cometido en el dispositivo m&oacute;vil o emulador.</p>
<p>No se olvida Flutter tampoco de la importancia de los <strong>tests autom&aacute;ticos</strong>. El paquete incluye APIs diferenciadas para desarrollar tests unitarios y de integraci&oacute;n, junto con un ejecutor de pruebas que funciona en Windows, Linux y Mac sin necesidad de dispositivos o emuladores, ideal para operar en entornos de integraci&oacute;n continua.</p>
<p> </p>
<h2>Ens&eacute;&ntilde;ame la pasta</h2>
<p>Las aplicaciones Flutter se escriben en <strong>Dart</strong>, un lenguaje de programaci&oacute;n creado por Google, originalmente para desarrollo web. Dart es orientado a objetos, tipado y transpilable a JavaScript. Fue aprobado como est&aacute;ndar ECMA en 2015.</p>
<p>Veamos el aspecto de Dart con un cl&aacute;sico.</p>
<pre>
import 'package:flutter/material.dart';
void main() =&gt; runApp(new MyApp());
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Welcome to Flutter',
      home: new Scaffold(
        appBar: new AppBar(
          title: const Text('Welcome to Flutter'),
        ),
        body: const Center(
          child: const Text('Hello World'),
        ),
      ),
    );
  }
}
</pre>
<br />
<p>El ejemplo anterior muestra una app basada en Material Design. La primera l&iacute;nea importa el conjunto de widgets de Flutter que implementan este estilo. En la segunda aparece el m&eacute;todo <em>main()</em>, punto de entrada en la aplicaci&oacute;n, haciendo uso de la <em>notaci&oacute;n =&gt;</em> para definir su cuerpo en una sola l&iacute;nea.</p>
<p>Dicha l&iacute;nea crea un &uacute;nico widget que aloja toda la aplicaci&oacute;n. <strong>En Flutter casi todos los elementos imaginables en una interfaz de usuario ser&aacute;n widgets</strong>. Los widgets de Flutter son componentes reactivos dise&ntilde;ados para favorecer la composici&oacute;n, y van m&aacute;s all&aacute; de lo que se asocia habitualmente a la palabra &lsquo;widget&rsquo; en otros sistemas, incluyendo conceptos como alineaciones o m&aacute;rgenes.</p>
<p>En el ejemplo se define la clase <em>MyApp</em> como un <strong>widget sin estado</strong>, que sirve como ra&iacute;z de la interfaz. De <em>MyApp</em> pende un widget de tipo <em>Scaffold</em>, un elemento espec&iacute;fico de la biblioteca Material que acepta una barra de aplicaci&oacute;n con un t&iacute;tulo y una propiedad <em>body</em> para alojar el contenido principal de la vista. En este caso, el <em>body</em> consiste de un widget <em>Center</em> que alinea a su widget hijo <em>Text</em> en el centro de la pantalla.</p>
<p>Para ver este c&oacute;digo en acci&oacute;n, sigue las instrucciones <a href="https://flutter.dev/docs/get-started/codelab" rel="noreferrer noopener" target="_blank">aqu&iacute;</a>. El ejemplo est&aacute; copiado del primer paso en un peque&ntilde;o tutorial de la documentaci&oacute;n oficial de Flutter.</p>
<h2>Dar cera, pulir cera</h2>
<p>Fin del publirreportaje. A pesar del entusiasmo de Google y la comunidad de Flutter, a&uacute;n hay mucho que demostrar. En todas las soluciones multiplataforma la pregunta fundamental es la misma: <strong>&iquest;hasta que punto puedo reemplazar mis dos entornos de desarrollo nativos por esta nueva y flamante soluci&oacute;n?</strong></p>
<p>Flutter viene acompa&ntilde;ado por un <strong>sistema de interoperabilidad y extensi&oacute;n</strong> que permite acceder a la interfaz de programaci&oacute;n de la plataforma desde el c&oacute;digo escrito en Dart. Dicho sistema sirve como base para diversos paquetes de extensi&oacute;n publicados en <a href="https://pub.dartlang.org/flutter" rel="noreferrer noopener" target="_blank">Dartlang.org</a> que son f&aacute;cilmente integrables en las apps gracias a la herramienta de gesti&oacute;n de dependencias inclu&iacute;da con el SDK. Todo un ecosistema de bibliotecas a tiro de piedra.</p>
<p>La cuesti&oacute;n es que <strong>dichos paquetes no son Flutter</strong>, y por tanto el fabuloso, s&oacute;lido y confiable 1.0 que acompa&ntilde;a al sistema no aplica a los mismos. Cada paquete tiene su propio conjunto de colaboradores, su propio estado de madurez y su propio n&uacute;mero de versi&oacute;n. A priori, es dif&iacute;cil poner la misma confianza en un <a href="https://pub.dartlang.org/packages/camera" rel="noreferrer noopener" target="_blank">paquete de acceso a c&aacute;mara</a> que luce una versi&oacute;n 0.4.2 que en las interfaces directas del sistema base.</p>
<p><a href="https://flutter.dev/docs/resources/faq#can-i-access-platform-services-and-apis-like-sensors-and-local-storage" rel="noreferrer noopener" target="_blank">Flutter nos anima a nosotros</a>, desarrolladores, a crear nuestras propias extensiones y a sumarnos a la comunidad de colaboradores. Sin duda es un planteamiento fant&aacute;stico para potenciar el crecimiento de la plataforma. Sin embargo, suma un punto de incertidumbre acerca de su estabilidad y evoluci&oacute;n a corto plazo que un desarrollador responsable no puede ignorar.</p>
<p><img class="wp-image-7372" src="https://solidgeargroup.com/wp-content/uploads/2019/03/03_can_flutter_cannot_fly.jpg" alt="Can flutter, cannot fly" /></p>
<p>Por otro lado, la independencia de los componentes de interfaz nativos trae sus propios inconvenientes. Hay un <strong>nuevo cat&aacute;logo de widgets que aprender</strong>, con el tiempo y esfuerzo que ello requiere. Dicho cat&aacute;logo parece realmente completo, pero compite con dos que llevan mucho tiempo afianzados, y no podemos dar por sentado que nuestros componentes favoritos tendr&aacute;n un widget equivalente disponible. Adem&aacute;s, los puristas no dudar&aacute;n en recordarnos que los widgets de Flutter s&oacute;lo imitan los estilos nativos.</p>
<p>En cuanto a las herramientas, la jaula dorada de Apple favorece una asimetr&iacute;a totalmente esperable. Si bien <strong>Xcode</strong> es un requisito para poder generar las apps de Flutter para iOS, no hay forma alguna de desarrollar las mismas en &eacute;l. La transici&oacute;n se antoja mucho m&aacute;s sencilla para desarrolladores de Android o Xamarin al existir extensiones para sus IDEs habituales.</p>
<p>Merece menci&oacute;n especial la ausencia de un sistema de inyecci&oacute;n de indepencias. <a href="https://flutter.dev/docs/resources/faq#does-flutter-come-with-a-dependency-injection-framework-or-solution" rel="noreferrer noopener" target="_blank">Searching for friends</a>.</p>
<h2>Hakuna matata</h2>
<p>&iquest;Merece la pena dar el salto? Pues mira, yo que s&eacute;. El desarrollo de apps multiplataforma se ha convertido en el Santo Grial del desarrollo m&oacute;vil, y bien sabemos que hasta el momento es mitad realidad, mitad leyenda.</p>
<p>A primera vista Flutter parece lo bastante s&oacute;lido como para funcionar bien en <strong>prototipado r&aacute;pido o clientes REST sencillos</strong>, pero guarda mucha incertidumbre como para llevar a producci&oacute;n apps m&aacute;s sofisticadas.</p>
<p>Esto lo coloca en una situaci&oacute;n similar a otros sistemas multiplataforma, con la desventaja de que llega m&aacute;s tarde, lo que dificulta justificar el cambio si ya se cuenta con <a href="https://solidgeargroup.com/integracion-de-firmado-manual-en-aplicaciones-xamarin-forms?lang=es" rel="noreferrer noopener" target="_blank">experiencia</a> <a href="https://solidgeargroup.com/desarrollo-de-apps-hibridas-con-ionic?lang=es" rel="noreferrer noopener" target="_blank">en alguno</a> <a href="https://solidgeargroup.com/como-anadir-uitests-en-un-aplicacion-en-xamarin?lang=es" rel="noreferrer noopener" target="_blank">de ellos</a>. Quiz&aacute; el rendimiento optimizado que promete marque la diferencia, pero valorar esto en profundidad va m&aacute;s all&aacute; de lo que podemos estimar con una primera aproximaci&oacute;n.</p>
<p>Otro incentivo interesante es que Flutter sea la primera plataforma de desarrollo de apps conocida para <strong><a href="https://en.wikipedia.org/wiki/Google_Fuchsia" rel="noreferrer noopener" target="_blank">Google Fuchsia</a>,</strong> el sistema operativo para dominarlos a todos. Posicionar temprano tus apps en Fuchsia supondr&iacute;a una ventaja competitiva importante si el proyecto llegara a buen t&eacute;rmino. As&iacute; parec&iacute;a, al menos, hasta que se supo que <a href="https://www.xataka.com/aplicaciones/google-encontro-forma-perfecta-todos-adopten-fuchsia-os-anadiendo-compatibilidad-para-apps-android" rel="noreferrer noopener" target="_blank">las apps de Android estar&aacute;n soportadas por el sistema</a>. Y no olvidemos que Google cierra proyectos como el que cierra la puerta de su casa. Ciao, Google+.</p>
<p>En definitiva, <strong>val&oacute;ralo t&uacute; mismo</strong>. Si te apetece echar un vistazo hay mucho contenido interesante en la <a href="https://flutter.dev/docs" rel="noreferrer noopener" target="_blank">documentaci&oacute;n oficial</a>, tambi&eacute;n en <a href="https://flutter-es.io/docs" rel="noreferrer noopener" target="_blank">espa&ntilde;ol</a>. Los <a href="https://codelabs.developers.google.com/?cat=Flutter" rel="noreferrer noopener" target="_blank">Codelabs sobre Flutter</a> son particularmente apropiados para iniciarse.</p>
<p>Nosotros seguiremos con un ojo puesto en la plataforma, pero por el momento tenemos claro que hay otras cestas en las que poner parte de los huevos.</p>
<p><img class="wp-image-7376" src="https://solidgeargroup.com/wp-content/uploads/2019/03/04_flutter_in_the_sun.jpg" alt="Flutter in the sun" /></p>]]></content:encoded>
</item><item>
<title><![CDATA[Integración continua en Golang]]></title>
<link>https://betabeers.com/blog/integracion-continua-golang-396/</link>
<pubDate>Fri, 15 Mar 2019 10:17:59 +0000</pubDate>
<category>Colaboraciones</category>
<content:encoded><![CDATA[<p><em>Este art&iacute;culo ha sido escrito por <strong>Adri&aacute;n P&eacute;rez</strong> y publicado originalmente en el blog de <a href="https://blog.friendsofgo.tech/posts/integracion_continua_en_golang/">Friends of Go</a></em></p>
<p>Para este art&iacute;culo vamos a utilizar la API, <a href="https://github.com/friendsofgo/gopherapi">GopherApi</a>, que es de software libre y cualquier persona puede realizar PRs (Pull Request) sobre ella, con lo cu&aacute;l necesitamos un sistema que nos verifique que los PRs cumplen con unos est&aacute;ndares puestos por nosotros para asegurarnos que al integrarlos en la rama <em>master</em> no se produzca ning&uacute;n error.</p>
<h2 id="queslaintegracincontinuaci">&iquest;Qu&eacute; es la Integraci&oacute;n continua(CI)?</h2>
<p>La <strong>integraci&oacute;n continua</strong> o <strong>continuos integration</strong> es una pr&aacute;ctica de desarrollo, propuesta inicialmente por <em>Martin Fowler</em> que consiste en realizar <strong>integraciones autom&aacute;ticas</strong> de un proyecto lo m&aacute;s a menudo posible, para as&iacute; poder detectar fallos lo antes posible. Entendemos por integraci&oacute;n la compilaci&oacute;n y ejecuci&oacute;n de pruebas de todo el proyecto.</p>
<p>En este art&iacute;culo trataremos la primera parte de la <strong>integraci&oacute;n continua</strong> que ser&aacute; la comprobaci&oacute;n de que todos los <em>PRs (Pull Request)</em> que reciba nuestro proyecto sean verificados por un proceso que nosotros realizaremos, asegur&aacute;ndonos tras una revisi&oacute;n manual de los cambios que dicho c&oacute;digo no tiene afectaciones colaterales.</p>
<h2 id="ospresentamoscircleci">Os presentamos Circle CI</h2>
<p><img style="display: block; margin-left: auto; margin-right: auto;" src="http://betabeers.com/uploads/blog/20190315_IMG_4631.PNG" alt="" width="400" height="400" /></p>
<p>Hasta hoy en d&iacute;a, la herramienta que se utilizaba por predilecci&oacute;n de los developers que tienen c&oacute;digo open source en <em>github</em> era <em><a href="https://travis-ci.org/">TravisCI</a></em>, pero tras ser comprado por <em>Idera</em> y despedir a gran parte de la plantilla, la comunidad no se siente c&oacute;moda utilizando dicha herramienta, por eso muchos han migrado a <a href="https://circleci.com/"><em>Circle CI</em></a> herramienta la cual os explicaremos hoy. En pr&oacute;ximos art&iacute;culos os explicaremos como migrar a <em>CircleCI</em> desde <em>TravisCI</em></p>
<p>Lo primero de todo ser&aacute; configurar nuestro proyecto en la p&aacute;gina de <em>CircleCi</em> no entraremos en detalle de este apartado, porque la propia p&aacute;gina es lo suficientemente intuitiva para realizar el proceso sin ayuda, igualmente si ten&eacute;is problemas recordar, que nos lo pod&eacute;is dejar en los comentarios o v&iacute;a twitter <a href="https://twitter.com/friendsofgotech">@FriendsofGoTech</a></p>
<h2 id="configurandonuestroproyecto">Configurando nuestro proyecto</h2>
<p>Primeramente deberemos crear un nuevo directorio llamado <code>.circleci</code> y dentro a&ntilde;adiremos el fichero <code>config.yml</code>, nos quedar&aacute; la siguiente ruta <code>.circleci/config.yml</code></p>
<p>Si hab&eacute;is seguido los pasos desde la propia web, tendr&eacute;is un fichero de ejemplo similar al siguiente:</p>
<p><img src="http://betabeers.com/uploads/blog/20190314_config_standard_yml (1).png" alt="configuracion circleci" /></p>
<p>Vamos a analizar este fichero por partes.</p>
<p>Obviando el tag de <code>version</code>, nos encontramos con el tag <code>jobs</code>, los <code>jobs</code> son esas tareas que se encargar&aacute; de ejecutar nuestro proceso, ser&aacute;n <strong>ejecutadas en un container de docker</strong> con la imagen que nosotros especifiquemos y con los scripts que queramos realizar.</p>
<p>Por defecto viene preparado para s&oacute;lo ejecutar un job, con una imagen por defecto de <strong>Go</strong> que es la <em>1.9</em>.</p>
<p>Aqu&iacute; tendremos que decidir con que versiones de <strong>Go</strong> es compatible nuestro proyecto, nosotros como sabemos que hemos hecho <em>GopherAPI</em> en la versi&oacute;n <em>1.11.4</em>, tendremos que asegurarnos de que nuestro proyecto sea compatible con esta versi&oacute;n y las venideras, veamos como queda nuestro fichero:</p>
<p><img src="http://betabeers.com/uploads/blog/20190314_config_go.png" alt="configuracion workflow ci" /></p>
<p>Lo que hemos hecho en el siguiente ejemplo ha sido, crear dos <em>jobs</em> distintos <code>build-go1.11.4</code> y <code>build-go_latest</code> (a los jobs podemos llamarlos como queramos, pero siempre es recomendable ponerle un nombre identificativo). Cada <em>job</em> ejecuta el mismo c&oacute;digo pero sobre diferentes versiones de go.</p>
<p>Por &uacute;ltimo nos encontramos con un nuevo bloque <code>workflows</code></p>
<p><img src="http://betabeers.com/uploads/blog/20190314_circle_ci_jobs.png" alt="configuracion jobs circleci" /></p>
<p>Aqu&iacute; simplemente hemos configurado, un proceso de trabajo, para que ejecute nuestros dos <em>jobs</em> de <strong>manera paralela</strong> es decir que lance los dos procesos a la vez, y de esta forma el tiempo sea inferior a si tenemos que esperar a que acabe uno para que empiece el otro.</p>
<p>Una vez tenemos nuestro fichero configurado, ya podremos hacer nuestro push a github y comprobar desde la p&aacute;gina de <em>CircleCI</em> que todo ha ido bien, adem&aacute;s como ya pasaba con <em>TravisCI</em> tendremos un badge que podremos poner en nuestro repositorio, que informar&aacute; a todos los consumidores de nuestro proyecto de si el c&oacute;digo que tenemos pasa aquellas pruebas que hemos propuesto en el fichero de configuraci&oacute;n (normalmente los tests y compilaci&oacute;n)</p>
<p>Ten&eacute;is el ejemplo pr&aacute;ctico en el repositorio de <a href="https://github.com/friendsofgo/gopherapi">Github</a> en la release <em><a href="https://github.com/friendsofgo/gopherapi/releases/tag/v0.2.1">v0.2.1</a></em>.</p>]]></content:encoded>
</item><item>
<title><![CDATA[Architecture Components: Repository y DataSources #AndroidMeetsKotlin]]></title>
<link>https://betabeers.com/blog/architecture-components-repository-datasources-androidmeetskotlin-395/</link>
<pubDate>Thu, 07 Mar 2019 23:22:44 +0000</pubDate>
<category>Colaboraciones</category>
<content:encoded><![CDATA[<h1 id="h.bemdan7ak0wd" class="c5"><img style="display: block; margin-left: auto; margin-right: auto;" src="http://betabeers.com/uploads/blog/20190307_imagen_1.png" alt="" width="550" height="303" /></h1>
<p class="c2"><span>Siguiendo con todo lo descrito en el </span><span class="c11">anterior post</span><span class="c0"> que abordaba <a href="https://www.google.com/url?q=https://betabeers.com/blog/architecture-components-livedata-viewmodel-androidmeetskotlin-392/&amp;sa=D&amp;ust=1551999815552000">la implementaci&oacute;n de LiveData y ViewModel en el patr&oacute;n MVVM</a>, ahora vas a conocer cu&aacute;l es la funci&oacute;n de un Repositorio y c&oacute;mo proporciona los datos a los ViewModel y &eacute;stos a su vez a la vista. Sin m&aacute;s, &iexcl;A por ello!</span></p>
<h2 id="h.1w1xau6gdsrz" class="c13"><span class="c6">Capa a definir</span></h2>
<p class="c2 c7"> </p>
<p class="c15"><img style="display: block; margin-left: auto; margin-right: auto;" src="http://betabeers.com/uploads/blog/20190307_imagen_2.png" alt="" width="400" height="345" /></p>
<p class="c2 c7"> </p>
<p class="c2"><span class="c0">Como puedes ver en la imagen anterior, el Repositorio ser&aacute; una capa que abstrae a los ViewModel del origen de los datos. Esta separaci&oacute;n de responsabilidad proporciona una mejor trazabilidad ante posibles errores, mejora la posible escalabilidad de tu aplicaci&oacute;n y la adaptaci&oacute;n a cambios estructurales o de implementaci&oacute;n. De esta forma, el ViewModel s&oacute;lo se preocupa de solicitar el listado de elementos y es el repositorio el que toma la decisi&oacute;n de proporcionarselo desde los distintos or&iacute;genes de datos que tenga (internet, cache, local, sharedPreferences, etc). </span></p>
<p class="c2"><span>Es importante destacar que se deber&iacute;a tener un repositorio y sus distintos data source por cada grupo de datos que tenga la app. Adem&aacute;s, cada ViewModel debe estar asociado a una Activity ya que de esta forma separamos responsabilidades y mantenemos los indicado en </span><span class="c11"><a class="c4" href="https://www.google.com/url?q=https://developer.android.com/topic/libraries/architecture/viewmodel&amp;sa=D&amp;ust=1551999815553000">Android Developers</a></span><span class="c0">. En el caso del repositorio, varios ViewModel pueden nutrirse del mismo repositorio.</span></p>
<p class="c2"><span>En cuanto a la implementaci&oacute;n, vas a tomar como referencia el proyecto que hiciste en el anterior post y vamos a implementar esta capa a partir de &eacute;l. El c&oacute;digo de este post puedes encontrarlo en la </span><span class="c11"><a class="c4" href="https://www.google.com/url?q=https://github.com/franlopjur/ArchitectureComponentsSample/tree/repository&amp;sa=D&amp;ust=1551999815553000">rama &ldquo;repository&rdquo; del proyecto</a></span><span class="c0">. Antes de ver la implementaci&oacute;n del repositorio, debes ver qu&eacute; es un DataSource y su implementaci&oacute;n.</span></p>
<p class="c2 c7"> </p>
<h2 id="h.zg6qnewqrvl8" class="c13"><span class="c6">DataSource</span></h2>
<p class="c2"><span class="c0">Se trata de un origen de datos del repositorio, en este caso tienes dos or&iacute;genes posibles:</span></p>
<ul class="c9 lst-kix_3n4y03ypwsy9-0 start">
<li class="c1"><strong><span class="c3">Local</span></strong><span>, llamado </span><strong><span class="c3">GithubRepoLocalDataSource</span></strong><span>. Tendr&aacute; el control de los accesos a base de datos y &eacute;sta ser&aacute; implementada con </span><strong><span class="c3">Room </span></strong><span>(informaci&oacute;n disponible en el siguiente </span><span class="c11"><a class="c4" href="https://www.google.com/url?q=https://betabeers.com/blog/room-otra-forma-crear-bases-datos-android-androidmeetskotlin-356/&amp;sa=D&amp;ust=1551999815554000">post de Fran L&oacute;pez que habla sobre una primera implementaci&oacute;n de esta librer&iacute;a</a></span><span>)</span><span class="c0">.</span></li>
<li class="c1"><strong><span class="c3">API</span></strong><span>, llamado </span><strong><span class="c3">GithubRepoApiDataSource</span></strong><span>. Se trata de la implementaci&oacute;n de la capa de servicios y usaremos </span><strong><span class="c3">Retrofit </span></strong><span>(informaci&oacute;n disponible en el siguiente </span><span class="c11"><a class="c4" href="https://www.google.com/url?q=https://betabeers.com/blog/kotlin-un-paso-mas-retrofit-jobqueue-kotlinmeetsandroid-331/&amp;sa=D&amp;ust=1551999815555000">post de Fran L&oacute;pez que habla sobre Kotlin y Retrofit</a></span><span>)</span><span class="c0">.</span></li>
</ul>
<p class="c2 c14"> </p>
<p class="c2"><span class="c0">A continuaci&oacute;n, ver&aacute;s la implementaci&oacute;n de los distintos DataSource.</span></p>
<h2 id="h.rgpj5k2h1631" class="c13"><span class="c6">GithubRepoLocalDataSource</span></h2>
<p class="c2"><span class="c0">Debes implementar dos m&eacute;todos:</span></p>
<ul class="c9 lst-kix_yrawq9rbhj0l-0 start">
<li class="c1"><strong><span class="c3">getGithubRepos(query: String): LiveData&lt;List&gt;</span></strong><span class="c0">. Accede al DAO de la BD y solicita los datos mediante una query de Room. Puesto que tiene una consulta con un &ldquo;LIKE&rdquo; es necesario a&ntilde;adirle el car&aacute;cter &ldquo;%&rdquo; al inicio y final para realizar la consulta de forma correcta.</span></li>
</ul>
<p class="c2 c14"><img src="http://betabeers.com/uploads/blog/20190307_imagen_3.png" alt="" width="550" height="76" /></p>
<ul class="c9 lst-kix_yrawq9rbhj0l-0">
<li class="c1"><strong><span class="c3">insert(list: List)</span></strong><span class="c0">. Inserta los elementos en la BD en background.</span></li>
</ul>
<p class="c2 c14"><img src="http://betabeers.com/uploads/blog/20190307_imagen_4.png" alt="" width="550" height="130" /></p>
<p class="c2 c7"> </p>
<p class="c2"><span>El c&oacute;digo lo puedes encontrar en el siguiente </span><span class="c11"><a class="c4" href="https://www.google.com/url?q=https://github.com/franlopjur/ArchitectureComponentsSample/blob/repository/app/src/main/java/com/betabeers/architecturecomponentsexample/data/GithubRepoLocalDataSource.kt&amp;sa=D&amp;ust=1551999815556000">enlace</a></span><span>.</span></p>
<h2 id="h.kqrheb6tcy53" class="c13"><span>GithubRepoApiDataSource</span></h2>
<p class="c2"><span class="c0">Debes implementar un &uacute;nico m&eacute;todo:</span></p>
<ul class="c9 lst-kix_bka3pgntx1no-0 start">
<li class="c1"><strong><span class="c3">getGithubRepos(query: String, success: (response: List) -&gt; Unit, failure: (errorMessage: String) -&gt; Unit). </span></strong><span class="c0">Realizar&aacute; la llamada a servicios y retorna los datos correctos en la funci&oacute;n &ldquo;success&rdquo; y un mensaje de error en la funci&oacute;n &ldquo;failure&rdquo; si la llamada ha provocado un error.</span></li>
</ul>
<p class="c2"><img src="http://betabeers.com/uploads/blog/20190307_imagen_5.png" alt="" width="650" height="272" /></p>
<p class="c2 c7"> </p>
<p class="c2"><span>El c&oacute;digo lo puedes encontrar en el siguiente </span><span class="c11"><a class="c4" href="https://www.google.com/url?q=https://github.com/franlopjur/ArchitectureComponentsSample/blob/repository/app/src/main/java/com/betabeers/architecturecomponentsexample/data/GithubRepoApiDataSource.kt&amp;sa=D&amp;ust=1551999815558000">enlace</a></span><span class="c0">.</span></p>
<h2 id="h.mlb37kz50dpl" class="c13"><span class="c6">Repositorio</span></h2>
<p class="c2"><span class="c0">Las variables que debes crear:</span></p>
<ul class="c9 lst-kix_uz734c3yc5ow-0 start">
<li class="c1"><strong><span class="c3">repoApiDataSource: GithubRepoApiDataSource</span></strong><span class="c0">. Debes inicializarlo con su constructor sin par&aacute;metros.</span></li>
<li class="c1"><strong><span class="c3">repoLocalCacheDataSource: GithubRepoLocalDataSource</span></strong><span class="c0">. Igual que el caso anterior.</span></li>
<li class="c1"><strong><span class="c3">isRequesting: Boolean</span></strong><span class="c0">. Debes inicializarlo a false.</span></li>
<li class="c1"><strong><span class="c3">errorLiveData: MutableLiveData</span></strong><span class="c0">. Inicializado con el constructor sin par&aacute;metros de MutableLiveData.</span></li>
</ul>
<p class="c2 c7"> </p>
<p class="c2"><span class="c0">La l&oacute;gica que vas a implementar en este repositorio realiza la llamada al servicio e inmediatamente retorna la consulta de la base de datos, de esta forma obtienes el LiveData proporcionado por Room. La consulta al servicio tienes que hacerla usando el DataSource de API y el de la BD con el DataSource de local.</span></p>
<p class="c2"><span>Una vez que la llamada a servicio ha retornado los datos, debes insertarlos en la BD y esto har&aacute; que el LiveData asociado se actualice de forma autom&aacute;tica</span><span class="c0">. En caso de que la llamada falle, se incluir&aacute; la informaci&oacute;n en el LiveData de error para que se informe a la vista.</span></p>
<p class="c2 c7"> </p>
<p class="c2"><span class="c0">En cuanto a m&eacute;todos, s&oacute;lo tendr&aacute; uno:</span></p>
<ul class="c9 lst-kix_qduz5fkvlkzk-0 start">
<li class="c1"><strong><span class="c3">searchRepos(query: String) : LiveData&lt;List&gt;</span><span class="c0">.</span></strong></li>
</ul>
<p class="c2 c14"><img src="http://betabeers.com/uploads/blog/20190307_imagen_6.png" alt="" width="550" height="226" /></p>
<p class="c2 c7"> </p>
<p class="c2 c7"> </p>
<p class="c2"><span>La implementaci&oacute;n del repositorio lo tienes en el siguiente </span><span class="c11"><a class="c4" href="https://www.google.com/url?q=https://github.com/franlopjur/ArchitectureComponentsSample/blob/repository/app/src/main/java/com/betabeers/architecturecomponentsexample/data/GithubRepository.kt&amp;sa=D&amp;ust=1551999815560000">enlace</a></span><span class="c0">.</span></p>
<h2 id="h.is862g3gow4y" class="c13"><span class="c6">SearchRepoViewModel</span></h2>
<p class="c2"><span class="c0">Lo primero que debes hacer es extender de la clase ViewModel. Esta clase tendr&aacute; cuatro variables:</span></p>
<ul class="c9 lst-kix_h0o4gujl2hz9-0 start">
<li class="c1"><strong><span class="c3">queryLiveData = MutableLiveData()</span></strong><span class="c0"><strong>.</strong> Almacenar&aacute; los cambios de la consulta realizada al pulsar el bot&oacute;n de &ldquo;Buscar&rdquo;.</span></li>
<li class="c1"><strong><span class="c3 c12">githubRepository: GithubRepository.</span></strong></li>
<li class="c1"><span class="c3"><strong>errorMessageLiveData: LiveData</strong>.</span><span class="c0"> Se encargar&aacute; de proporcionar a la vista la informaci&oacute;n actualizada de los errores en el servicio.</span></li>
<li class="c1"><span class="c3"><strong>githubReposListLiveData: LiveData&lt;List&gt;</strong>.</span><span> Para inicializarlo debes hacer uso de la funci&oacute;n </span><span class="c11"><a class="c4" href="https://www.google.com/url?q=https://developer.android.com/reference/android/arch/lifecycle/Transformations.html&amp;sa=D&amp;ust=1551999815562000">Transformations.switchMap</a></span><span class="c0">. Esta funci&oacute;n recibe como par&aacute;metro un LiveData al que observar&aacute; sus cambios con el fin de aplicar la funci&oacute;n que recibe como su segundo par&aacute;metro. De esta forma conseguiremos que la vista no tenga que registrarse y desregistrarse ante cambios en la consulta a la base de datos.</span></li>
</ul>
<p class="c2 c7"> </p>
<p class="c2"><img src="http://betabeers.com/uploads/blog/20190307_imagen_7.png" alt="" width="600" height="41" /></p>
<p class="c2 c7"> </p>
<p class="c2"><span class="c0">Adem&aacute;s, debes implementar dos m&eacute;todos:</span></p>
<ul class="c9 lst-kix_7inh2ls7bwis-0 start">
<li class="c1"><span class="c3"><strong>updateQuery(newQuery: String)</strong>.</span><span> Actualizar&aacute; el LiveData llamado &ldquo;</span><strong><span class="c3">queryLiveData</span></strong><span class="c0">&rdquo; el cu&aacute;l provocar&aacute; que se llame a la funci&oacute;n pasada en el m&eacute;todo &ldquo;switchMap&rdquo;.</span></li>
<li class="c1"><span class="c3"><strong>search(query: String): LiveData&lt;List&gt;</strong>.</span><span class="c0"> Llamar&aacute; al m&eacute;todo &ldquo;searchRepos(query)&rdquo; del repositorio para que realice la funci&oacute;n de obtener los datos.</span></li>
</ul>
<p class="c2 c7"> </p>
<p class="c2"><span>El c&oacute;digo del ViewModel lo puedes encontrar en el siguiente </span><span class="c11"><a class="c4" href="https://www.google.com/url?q=https://github.com/franlopjur/ArchitectureComponentsSample/blob/repository/app/src/main/java/com/betabeers/architecturecomponentsexample/ui/viewmodel/SearchRepoViewModel.kt&amp;sa=D&amp;ust=1551999815563000">enlace</a></span><span class="c0">.</span></p>
<h2 id="h.tbwducjh5phr" class="c13"><span class="c6">MainActivity</span></h2>
<p class="c2"><span class="c0">Por &uacute;ltimo, s&oacute;lo debes observar los LiveData del ViewModel desde la vista.</span></p>
<p class="c2 c7"> </p>
<p class="c2"><img src="http://betabeers.com/uploads/blog/20190307_imagen_8.png" alt="" width="600" height="217" /></p>
<p class="c2 c7"> </p>
<p class="c2"><span class="c0">De esta forma, al pulsar sobre el bot&oacute;n de &ldquo;Buscar&rdquo; se realizar&aacute; la llamada y se mostrar&aacute; en un Toast el n&uacute;mero de elementos.</span></p>
<p class="c2 c7"> </p>
<p class="c2"><span>Finalmente, en este post has visto una primera aproximaci&oacute;n de c&oacute;mo implementar un patr&oacute;n repositorio utilizando componentes de </span><span class="c11"><a class="c4" href="https://www.google.com/url?q=https://developer.android.com/topic/libraries/architecture&amp;sa=D&amp;ust=1551999815565000">Android Architecture Components</a></span><span> con un patr&oacute;n MVVM. En el siguiente post implementar&aacute;s una paginaci&oacute;n de este servicio, haremos usos de la librer&iacute;a </span><span class="c11"><a class="c4" href="https://www.google.com/url?q=https://developer.android.com/topic/libraries/architecture/paging&amp;sa=D&amp;ust=1551999815565000">Paging Library</a></span><span> y ver&aacute;s qu&eacute; puede aportar y c&oacute;mo puede simplificar la paginaci&oacute;n en tu aplicaci&oacute;n. El c&oacute;digo de este proyecto puedes encontrarlo en el siguiente </span><span class="c11"><a class="c4" href="https://www.google.com/url?q=https://github.com/franlopjur/ArchitectureComponentsSample/tree/repository&amp;sa=D&amp;ust=1551999815566000">enlace bajo la rama &ldquo;repository&rdquo;</a></span><span>. </span></p>
<p class="c2 c7"> </p>]]></content:encoded>
</item><item>
<title><![CDATA[Desarrollo WordPress en local con Docker]]></title>
<link>https://betabeers.com/blog/desarrollo-wordpress-local-docker-394/</link>
<pubDate>Tue, 05 Mar 2019 15:25:16 +0000</pubDate>
<category>Colaboraciones</category>
<content:encoded><![CDATA[<p>Desarrollar nuestros proyectos con WordPress puede ahorrarnos mucho tiempo al disponer de una extensa documentaci&oacute;n y de una gran comunidad que soporta el software, pero, como cualquier desarrollador no estamos exentos de problema derivados del propio entorno de trabajo que utilizamos o que compartimos.</p>
<p>Es m&aacute;s que habitual realizar las pruebas de funcionamiento en nuestro entorno local pero encontrarnos con incompatibilidades a la hora de subir el proyecto a producci&oacute;n o cualquier otro sistema. En muchas ocasiones es un punto muy complicado de salvar, pero la virtualizaci&oacute;n nos ofrece disponer de caracter&iacute;sticas comunes definidas a demanda para equiparar nuestro entorno local con otros escenarios. En este sentido, existen softwares como Docker que facilitan enormemente esta tarea de preparaci&oacute;n y despliegue de proyectos, incluidos aquellos basados en WordPress.</p>
<h2>&iquest;Qu&eacute; es Docker?</h2>
<p><a href="https://www.docker.com/" data-cke-saved-href="https://www.docker.com/">Docker</a> es un sistema de c&oacute;digo abierto que nos permite desplegar aplicaciones dentro de contenedores de software mediante la virtualizaci&oacute;n. En estos contenedores, que adem&aacute;s, son portables, podemos almacenar todos los elementos de software necesarios para poder hacer correr aquella aplicaci&oacute;n con la que queramos trabajar.</p>
<p>De este modo, independientemente de la m&aacute;quina matriz donde instalemos el contenedor, nuestra aplicaci&oacute;n podr&aacute; funcionar de modo id&eacute;ntico a como lo har&iacute;a en cualquier otro entorno.</p>
<p>Cuando hablamos de WordPress, podemos hacernos r&aacute;pidamente a la idea de las ventajas que nos podr&aacute; aportar a nuestros desarrollos especialmente si trabajamos con m&aacute;s profesionales en un mismo proyecto.</p>
<p><img style="display: block; margin-left: auto; margin-right: auto;" title="Desarrollo WordPress local Docker" src="http://betabeers.com/uploads/blog/20190305_desarrollo-wordpress-local-docker.jpg" alt="Desarrollo WordPress local Docker" width="600" height="400" /></p>
<h2>&iquest;Qu&eacute; ventajas tiene Docker para el desarrollo WordPress?</h2>
<p>Como desarrolladores de temas o plugins para WordPress podremos centrarnos en la creaci&oacute;n de c&oacute;digo sin preocuparnos de si funcionar&aacute; o no en el entorno de staging o producci&oacute;n.</p>
<p>Es m&aacute;s ligero que otros sistemas de virtualizaci&oacute;n como VirtualBox o VMware, y adem&aacute;s, en este caso hablamos de un sistema que adem&aacute;s de ser gratuito es open source. Siendo multiplataforma podemos hacerlo correr en nuestro sistema operativo independientemente de que sea Mac, Linux o Windows.</p>
<p>Mediante las im&aacute;genes y los repositorios podemos crear nuestros propios entornos de trabajo y compartirlos con aquellas personas con las que queramos trabajar en com&uacute;n, o directamente trabajar con sistemas publicados en abierto listos para utilizar.</p>
<p>Es extremadamente ligero, permitiendo gestionar diferentes contenedores de Docker en una misma m&aacute;quina, pudiendo realizar tests en diferentes entornos sin mayores complicaciones.</p>
<p>Podemos elegir el entorno de versiones que necesitemos por ejemplo para Apache y PHP, o a&ntilde;adir software para el desarrollo como <a href="../gestion-avanzada-wordpress-wp-cli-302/" data-cke-saved-href="https://betabeers.com/blog/gestion-avanzada-wordpress-wp-cli-302/">WP-CLI</a>, del que ya hemos hablado tiempo atr&aacute;s.</p>
<p>Si est&aacute;s desarrollando proyectos WordPress en local en este mismo momento, sin duda Docker es una de las opciones que deber&iacute;as plantearte para mejorar tu entorno de trabajo. Es posible que hayas probado opciones como WampServer, MAMP, DesktopServer u otras alternativas, pero Docker sin duda se presenta como la opci&oacute;n definitiva.</p>
<h2>&iquest;C&oacute;mo crear un contenedor Docker para WordPress?</h2>
<p>A continuaci&oacute;n har&eacute; un repaso de c&oacute;mo podemos instalar y configurar Docker con un contenedor WordPress para nuestros proyectos.</p>
<h3>Instalando Docker</h3>
<p>En primer lugar deberemos instalar Docker para nuestro sistema operativo, algo realmente sencillo puesto que disponemos de <a href="https://docs.docker.com/docker-for-mac/install/%20)%20o%20Windows%20(%20https://docs.docker.com/docker-for-windows/install/" data-cke-saved-href="https://docs.docker.com/docker-for-mac/install/ ) o Windows ( https://docs.docker.com/docker-for-windows/install/">instaladores para Mac</a> o <a href="https://docs.docker.com/docker-for-windows/install/" data-cke-saved-href="https://docs.docker.com/docker-for-windows/install/">Windows</a> que nos guiar&aacute;n en el proceso de instalaci&oacute;n.</p>
<h3>Instalando Compose Docker</h3>
<p>A continuaci&oacute;n, y para continuar con el entorno de trabajo que estamos preparando, a&ntilde;adiremos Compose para Docker siguiendo las instrucciones <a href="https://docs.docker.com/compose/install/" data-cke-saved-href="https://docs.docker.com/compose/install/">del sitio web oficial</a>. Compose nos permite crear aplicaciones con m&uacute;ltiples contenedores, con un fichero de configuraci&oacute;n previamente establecido y arrancar las mismas a trav&eacute;s de una serie de &oacute;rdenes sencillas desde la terminal.</p>
<p>Si est&aacute;s utilizando las &uacute;ltimas versiones de escritorio de Windows o Mac, debes saber que Compose ya se encuentra incluido en este tipo de instalaciones por lo que podr&aacute;s obviar el paso anterior</p>
<p>Para comprobar si est&aacute; instalado y la versi&oacute;n de Compose Docker que est&aacute;s utilizando puedes usar el siguiente comando en la terminal:</p>
<pre>docker-compose -v</pre>
<p>Para crear nuestro entorno WordPress, necesitaremos de dos contenedores principales. Uno de ellos deber&aacute; contener un servidor web, por ejemplo, Apache, y el otro la base de datos MySQL para hacer funcionar nuestra instalaci&oacute;n.</p>
<h3>Creando nuestro proyecto WordPress</h3>
<p>En primer lugar deberemos crear un directorio vac&iacute;o para nuestro proyecto. En este directorio se incluir&aacute; toda la informaci&oacute;n de nuestra imagen del proyecto y los contenedores necesarios.</p>
<p>Necesitaremos a continuaci&oacute;n un archivo docker-compose.yml con la configuraci&oacute;n completa para lanzar una instalaci&oacute;n de WordPress. El archivo &ldquo;.yml&rdquo; es el que define las instancias que vamos a incluir en nuestra imagen, siendo estas un servidor web con la instalaci&oacute;n de WordPress y un contenedor con la base de datos MySQL.</p>
<p>En este ejemplo que podemos utilizar tenemos toda la informaci&oacute;n necesaria:</p>
<pre>version: '3.3'

services:

  db:

    image: mysql:5.7

    volumes:

      - db_data:/var/lib/mysql

    restart: always

    environment:

      MYSQL_ROOT_PASSWORD: somewordpress

      MYSQL_DATABASE: wordpress

      MYSQL_USER: wordpress

      MYSQL_PASSWORD: wordpress

  wordpress:

    depends_on:

      - db

    image: wordpress:latest

    ports:

      - "8080:80"

    restart: always

    environment:

      WORDPRESS_DB_HOST: db:3306

      WORDPRESS_DB_USER: wordpress

      WORDPRESS_DB_PASSWORD: wordpress

      WORDPRESS_DB_NAME: wordpress

      </pre>
<p>Revisando el archivo de configuraci&oacute;n anterior nos encontramos con la la declaraci&oacute;n del contenedor de MySQL en su versi&oacute;n 5.7 del cual depende el contenedor de WordPress que lanzar&aacute; la &uacute;ltima versi&oacute;n estable del CMS.</p>
<p>El contenedor de WordPress es directamente descargado desde el repositorio oficial de Docker cuya imagen podemos consultar en el <a href="https://hub.docker.com/_/wordpress/" data-cke-saved-href="https://hub.docker.com/_/wordpress/">siguiente enlace</a>.</p>
<p>En el caso de que quer&aacute;is utilizar un fichero de configuraci&oacute;n con otras opciones m&aacute;s avanzadas os dejo algunos ejemplos de Github que incluyen contenedores alternativos, otras versiones, y algunas mejoras sobre el ejemplo incluido:</p>
<p><a href="https://github.com/chriszarate/docker-compose-wordpress/blob/master/docker-compose.yml" data-cke-saved-href="https://github.com/chriszarate/docker-compose-wordpress/blob/master/docker-compose.yml">https://github.com/chriszarate/docker-compose-wordpress/blob/master/docker-compose.yml</a></p>
<p><a href="https://github.com/nezhar/wordpress-docker-compose/blob/master/docker-compose.yml" data-cke-saved-href="https://github.com/nezhar/wordpress-docker-compose/blob/master/docker-compose.yml">https://github.com/nezhar/wordpress-docker-compose/blob/master/docker-compose.yml</a></p>
<p><a href="https://github.com/tfirdaus/wp-locker/blob/develop/docker-compose.yml" data-cke-saved-href="https://github.com/tfirdaus/wp-locker/blob/develop/docker-compose.yml">https://github.com/tfirdaus/wp-locker/blob/develop/docker-compose.yml</a></p>
<h3>Arrancar el contenedor</h3>
<p>Una vez que tenemos lista nuestra configuraci&oacute;n de Compose Docker vamos a arrancar la instalaci&oacute;n y descarga de todo lo necesario por primera vez mediante el siguiente comando que ejecutaremos en la terminal desde el propio directorio en el que estamos trabajando.</p>
<p>docker-compose up -d</p>
<p>Cuando finalice el proceso se lanzar&aacute; en nuestro navegador la direcci&oacute;n URL siguiente cargando la pantalla de bienvenida e instalaci&oacute;n de WordPress:</p>
<p><a href="http://localhost:8080/" data-cke-saved-href="http://localhost:8080">http://localhost:8080</a></p>
<p>Si queremos crear instancias con par&aacute;metros diferentes y que se encuentren trabajando al mismo tiempo, podemos simplemente repetir los pasos anteriores, modificando el fichero de configuraci&oacute;n &ldquo;.yml&rdquo;.</p>
<p>En este caso concreto, es interesante modificar los puertos de Apache y MySQL para evitar posibles conflictos, por lo que les asignaremos un valor diferente a medida que vayamos creando y lanzando contenedores en paralelo.</p>
<h2>Comando &uacute;tiles para Docker</h2>
<p>Desarrollando con Docker podemos automatizar algunas tareas o hacer que sean m&aacute;s sencillas de lo que podr&iacute;a parecer a la hora de trabajar en un entorno local. Te dejo algunos comandos de utilidad que a buen seguro tendr&aacute;s a bien utilizar.</p>
<pre>docker-compose pull
docker-compose up -d
</pre>
<p>Permite actualizar las versiones a la &uacute;ltima estable de nuestros contenedores, por ejemplo si queremos actualizar WordPress o MySQL:.</p>
<pre>docker-compose start</pre>
<p>Arranca los contenedores parados en el directorio en el que estamos trabajando.</p>
<pre>docker-compose stop</pre>
<p>Para los contenedores en funcionamiento en nuestro directorio de trabajo.</p>
<pre>docker-compose config</pre>
<p>Valida que la configuraci&oacute;n sea correcta y muestra el archivo de configuraci&oacute;n establecido.</p>
<pre>docker-compose ps</pre>
<p>Lista todos los contenedores incluidos en el directorio de trabajo.</p>
<pre>docker-compose down</pre>
<p>Detiene y elimina todos los contenedores, teniendo en cuenta que se restablecer&aacute; toda la informaci&oacute;n a su punto de partida, base de datos incluida.</p>
<h2>Conclusiones</h2>
<p>Si queremos ofrecer un mejor servicio a nuestros clientes, y si queremos ser realmente competitivos en nuestro trabajo debemos tener en cuenta que en ocasiones los peque&ntilde;os detalles son los que marcan la diferencia.</p>
<p>En este caso, poder disponer de un entorno local de trabajo adaptado a nuestras necesidades y que nos permita agilizar labores cotidianas en nuestros desarrollos de temas y plugins podr&aacute; hacer que podamos ocuparnos de otro tipo de problem&aacute;ticas m&aacute;s significativas en el proyecto que estamos abordando.</p>
<p>Adem&aacute;s, en un mundo conectado y donde trabajar en remoto est&aacute; al orden del d&iacute;a podemos salvar los problemas que nos encontramos con los diferentes entornos con herramientas como Docker.</p>
<p>Dicho esto, no tienes excusa para darle una oportunidad. No te arrepentir&aacute;s.</p>]]></content:encoded>
</item><item>
<title><![CDATA[Soporte a teclados externos en apps iOS]]></title>
<link>https://betabeers.com/blog/soporte-a-teclados-externos-apps-ios-393/</link>
<pubDate>Fri, 22 Feb 2019 10:25:10 +0000</pubDate>
<category>Colaboraciones</category>
<content:encoded><![CDATA[<p style="text-align: center;"> </p>
<p style="text-align: left;"><em>Este art&iacute;culo ha sido escrito por <a href="https://twitter.com/pablocarmu?lang=es">Pablo Carrascal</a><br /></em></p>
<p>27 de enero de 2010, Steve Jobs sube al escenario del _Yerba Buena Center for the Arts_ de San Francisco para presentar la primera generaci&oacute;n de un nuevo dispositivo, el iPad. Desde ese primer d&iacute;a, el iPad ha tenido gran importancia dentro del ecosistema de Apple, habiendo sufrido importantes cambios y variantes tanto en tama&ntilde;o como en caracter&iacute;sticas t&eacute;cnicas, llegando a competir de tu a tu contra PC y Mac en algunos casos.</p>
<p>Cada generaci&oacute;n de iPad venia acompa&ntilde;ada de accesorios, entre ellos un teclado. Sin embargo, todos los modelos, incluyendo el original, son compatibles con teclados bluetooth. Si bien esto es cierto, no ha sido hasta estas &uacute;ltimas generaciones en las que, gracias al potente hardware y a las sucesivas mejoras en iOS, el iPad con teclado se convierte en una herramienta de trabajo.</p>
<p style="text-align: center;"><img title="teclado-ios" src="http://betabeers.com/uploads/blog/20190222_mobile-apple-keyboard-technology-black-iphone-7-482450-pxhere.com.jpg" alt="teclado-ios" width="770" height="513" /></p>
<p>Desde el punto de vista de desarrollo, a&ntilde;adir soporte a tu applicaci&oacute;n para teclados externos/bluetooth es sencillo, para ello usaremos las APIs de `UIResponder`.</p>
<p>Los objectos `responder`, que son instancias de `UIResponder`, constituyen la columna vertebral del manejo de eventos en `UIKit`. Las principales clases dentro de `UIKit` tales como `UIApplication`, `UIViewController` y `UIView` son tambien objetos `responder`. A medida que un evento ocurre, `UIKit` lo envia a los `responder` de la app para que lo traten.</p>
<p>En concreto, dentro de la variada API de `UIResponder`, para dar soporte a teclados externos nos interesa la variable `var keyCommands: [UIKeyCommand]?`, la cual representa los comandos de teclas que ejectutan una acci&oacute;n en ese `UIResponder`.</p>
<p>Cuando ocurre una combinaci&oacute;n de teclas que encaja con un `keyCommand`, `UIKit` busca por la cadena de `responders` un objeto que encaje con esa combinaci&oacute;n, despu&eacute;s, llama al primer objeto que encuentra y para el procesamiento del evento.</p>
<p>Para crear un `UIKeyCommand` utilizaremos el inicializador `init(input: String, modifierFlags: UIKeyModifierFlags, action: Selector)`</p>
<p>siendo:</p>
<ul>
<li>_input_: Identificador la tecla presionada.</li>
<li>_modifierFlags_: Teclas modificadoras tales como Shift, control&hellip;</li>
<li>_action_: Funci&oacute;n a ejecutar</li>
</ul>
<pre class="wp-block-code"><code>override var keyCommands: [UIKeyCommand]? {

    let deleteInputKey: String = "\u{8}"
        
        var keyCommands: [UIKeyCommand] = []
        
        keyCommands.append(
            UIKeyCommand(input: "\u{8}",
                         modifierFlags: [],
                         action: #selector(deleteKeyPressed),
                         discoverabilityTitle: "Delete")
        )
        
        
         keyCommands.append(          
             UIKeyCommand(input: UIKeyCommand.inputEscape,
                          modifierFlags: [],
                          action: #selector(escapeKeyPressed),
                          discoverabilityTitle: "Cancel")
         )
        
        
        return keyCommands
    }</code></pre>
<p>La API de UIKeyCommand es muy variada y sencilla a la vez, permitiendo de manera r&aacute;pida a&ntilde;adir incluso atajos de teclado:</p>
<pre class="wp-block-code"><code>override var keyCommands: [UIKeyCommand]? {
var keyCommands: [UIKeyCommand] = []

// Comando + Escape
keyCommands.append(
UIKeyCommand(input: UIKeyCommand.inputEscape",
modifierFlags: [.command],
   action: #selector(deleteKeyPressed),
discoverabilityTitle: "Command + Escape")
)

return keyCommands
}
</code></pre>
<p>Si bien esta API es bastante potente y sencilla, tiene limitaciones, como por ejemplo saber cuando el usuario suelta una tecla.</p>
<p>Espero que habiendo mostrado lo facil que es a&ntilde;adir soporte para teclados externos en iOS, cada vez m&aacute;s aplicaciones se animen a soportarlo.</p>]]></content:encoded>
</item><item>
<title><![CDATA[Architecture Components: LiveData y ViewModel #AndroidMeetsKotlin]]></title>
<link>https://betabeers.com/blog/architecture-components-livedata-viewmodel-androidmeetskotlin-392/</link>
<pubDate>Fri, 08 Feb 2019 01:40:56 +0000</pubDate>
<category>Colaboraciones</category>
<content:encoded><![CDATA[<h1 id="h.t8h5crk67jhp" class="c8"><img style="display: block; margin-left: auto; margin-right: auto;" src="http://betabeers.com/uploads/blog/20190208_image_1.png" alt="" width="500" height="275" /></h1>
<p class="c5"><span class="c0">Google est&aacute; empezando a marcar gu&iacute;as y dando consejos sobre la arquitectura a definir en las aplicaciones que vas a desarrollar o mejorar. Por este motivo, nace el concepto de &ldquo;Architecture Components&rdquo;, se trata de todos los componentes, herramientas y &uacute;tiles que permitir&aacute;n desarrollar su arquitectura planteada de una forma correcta y marcando un est&aacute;ndar entre todos los desarrolladores. En este post, obtendr&aacute;s informaci&oacute;n sobre LiveData y ViewModel, los cuales son los elementos a utilizar entre nuestros Activities / Fragments y nuestro repositorio. Concretamente la arquitectura a desarrollar debe seguir el siguiente diagrama:</span></p>
<p class="c5 c12"> </p>
<p class="c9"><img style="display: block; margin-left: auto; margin-right: auto;" src="http://betabeers.com/uploads/blog/20190208_image_2.png" alt="" width="400" height="345" /></p>
<p class="c5 c12"> </p>
<p class="c5"><span>Este primer post est&aacute; encaminado en detallar lo inclu&iacute;do en el recuadro azul. En cuanto al elemento llamado &ldquo;Repositorio&rdquo;, s&oacute;lo veremos que har&aacute; la llamada a un servicio pero en posteriores post ir&aacute;s conociendo para qu&eacute; se usa y hasta d&oacute;nde llega su competencia dentro de esta arquitectura. Podr&aacute;s obtener m&aacute;s informaci&oacute;n en el siguiente </span><span class="c6"><a class="c7" href="https://www.google.com/url?q=https://developer.android.com/topic/libraries/architecture/&amp;sa=D&amp;ust=1549588881142000">enlace</a></span><span class="c0">.</span></p>
<p class="c5 c12"> </p>
<p class="c5"><span class="c0">Antes de nada, debes conocer para qu&eacute; se usan y cuales son las ventajas de LiveData y ViewModel:</span></p>
<h2 id="h.lnjw1sxrpuv" class="c1"><span class="c3">LiveData</span></h2>
<p class="c5"><span>En resumidas palabras, se trata de una clase que retiene la informaci&oacute;n de nuestros objetos y tiene la capacidad de ser observable. Lo m&aacute;s importante, y es lo que le distingue de otros posibles patrones observables, es la capacidad de adaptarse y optimizar el uso de los datos al ciclo de vida de Android. De esta forma, </span><span class="c6"><a class="c7" href="https://www.google.com/url?q=https://developer.android.com/topic/libraries/architecture/livedata&amp;sa=D&amp;ust=1549588881143000">LiveData </a></span><span class="c0">s&oacute;lo notificar&aacute; a los elementos suscritos si &eacute;stos est&aacute;n activos. </span></p>
<p class="c5"><span>&Eacute;ste era uno de los principales problemas del</span><span class="c6"><a class="c7" href="https://www.google.com/url?q=https://betabeers.com/blog/kotlin-primera-toma-contacto-mvp-android-androidmeetskotlin-338/&amp;sa=D&amp;ust=1549588881144000"> &ldquo;Presenter&rdquo; en el MVP</a></span><span class="c0">, por lo que gracias a este componente, nos quitamos este problema de un plumazo.</span></p>
<p class="c5 c12"> </p>
<p class="c5"><span class="c0">Resumiendo sus ventajas:</span></p>
<ul class="c4 lst-kix_bis2clj419lt-0 start">
<li class="c2"><span class="c0">Perfecta sincronizaci&oacute;n con el ciclo de vida de la UI.</span></li>
<li class="c2"><span class="c0">Adi&oacute;s a los MemoryLeaks (se libera cuando debe).</span></li>
<li class="c2"><span class="c0">Adi&oacute;s al control manual del ciclo de vida.</span></li>
<li class="c2"><span class="c0">Datos actualizados en todo momento.</span></li>
<li class="c2"><span class="c0">No se ven afectados por los cambios de configuraci&oacute;n.</span></li>
</ul>
<p class="c5 c12"> </p>
<p class="c5"><span>Es importante saber que LiveData es una clase abstracta, por lo que cuando quieras publicar datos en &eacute;l, debes usar la clase </span><span class="c6 c11"><a class="c7" href="https://www.google.com/url?q=https://developer.android.com/reference/android/arch/lifecycle/MutableLiveData&amp;sa=D&amp;ust=1549588881145000">MutableLiveData</a></span><span class="c0">. &Eacute;sta tiene dos m&eacute;todos:</span></p>
<ul class="c4 lst-kix_uoceaaky4c3-0 start">
<li class="c2"><span class="c11">setValue(T value)</span><span class="c0">. Este m&eacute;todo s&oacute;lo se puede ejecutar si sabemos que estamos en hilo principal, en otro caso, provocar&aacute; una excepci&oacute;n.</span></li>
<li class="c2"><span class="c11">postValue(T value)</span><span class="c0">. Este m&eacute;todo tardar&aacute; un poco en publicar los datos en nuestro LiveData pero lo har&aacute; de manera as&iacute;ncrona, por lo que no importa desde el hilo que se llame.</span></li>
</ul>
<p class="c5 c12"> </p>
<p class="c5"><span class="c0">Antes de poder empezar a usarlo, debes conocer qu&eacute; es un ViewModel.</span></p>
<h2 id="h.zhn49mi98dky" class="c1"><span class="c3">ViewModel</span></h2>
<p class="c5"><span class="c0">Se trata de una clase que est&aacute; dise&ntilde;ada para almacenar y administrar datos relacionados con la UI de forma optimizada para el ciclo de vida en Android.</span></p>
<p class="c5"><span class="c0">En la pr&aacute;ctica, lo usar&aacute;s para almacenar todos los LiveData que necesitar&aacute; tu vista (Fragment o Activity), de esta forma cuando los datos se actualicen se notificar&aacute; autom&aacute;ticamente a la vista y &eacute;sta se encargar&aacute; de pintarlos. El ciclo de vida de los ViewModel est&aacute; resumido en la siguiente imagen:</span></p>
<p class="c5 c12"> </p>
<p class="c9"><img style="display: block; margin-left: auto; margin-right: auto;" src="http://betabeers.com/uploads/blog/20190208_image_3.png" alt="" width="350" height="379" /></p>
<p class="c5 c12"> </p>
<p class="c5 c12"> </p>
<p class="c5"><span class="c0">Una vez que ya tenemos un poco claro qu&eacute; son estos componentes y para qu&eacute; se pueden usar, debes empezar con su implementaci&oacute;n. </span></p>
<p class="c5"><span>Puedes encontrar m&aacute;s informaci&oacute;n en el siguiente </span><span class="c6"><a class="c7" href="https://www.google.com/url?q=https://developer.android.com/topic/libraries/architecture/viewmodel&amp;sa=D&amp;ust=1549588881146000">enlace</a></span><span class="c0">.</span></p>
<h2 id="h.kyr65i1bippp" class="c1"><span class="c3">Definici&oacute;n del escenario</span></h2>
<p class="c5"><span>La app que desarrollar&aacute;s realizar&aacute; una llamada a un servicio (para que recuerdes c&oacute;mo se realizan llamadas a servicios puedes ver el siguiente </span><span class="c6"><a class="c7" href="https://www.google.com/url?q=https://betabeers.com/blog/kotlin-un-paso-mas-retrofit-jobqueue-kotlinmeetsandroid-331/&amp;sa=D&amp;ust=1549588881147000">post</a></span><span class="c0">) para obtener la lista de proyectos almacenados en Github bajo un nombre concreto, este par&aacute;metro ser&aacute; el que llamaremos de ahora en adelante &ldquo;query&rdquo;. </span></p>
<p class="c5 c12"> </p>
<p class="c5"><span class="c0">En la vista tendremos por el momento:</span></p>
<ul class="c4 lst-kix_evhglw74vf35-0 start">
<li class="c2"><span class="c0">Un campo editable.</span></li>
<li class="c2"><span class="c0">Un bot&oacute;n que al pulsarlo lanzar&aacute; la b&uacute;squeda.</span></li>
</ul>
<p class="c5 c12"> </p>
<p class="c5"><span class="c0">Cuando se reciban los datos del servicio se pintar&aacute; un toast con el tama&ntilde;o de la lista.</span></p>
<h2 id="h.39o824qbj4u4" class="c1"><span class="c3">Creamos el ViewModel con los LiveData necesarios</span></h2>
<p class="c5"><span class="c0">Debes crear 2 variables p&uacute;blicas y una privada.</span></p>
<ul class="c4 lst-kix_i6bo7apa6f9d-0 start">
<li class="c2"><span class="c11">private githubRepository: GithubRepository</span><span class="c0">. Se trata de la variable encargada de obtener los datos, ya sea de internet, cach&eacute; o base de datos. Este apartado no lo veremos en este post.</span></li>
<li class="c2"><span class="c11">errorMessageLiveData = MutableLiveData&lt;String&gt;()</span><span class="c0">. Se trata de un LiveData que informar&aacute; a la vista cuando haya un error al obtener los datos del repositorio. Nuestra vista debe suscribirse en &eacute;l para obtener los datos de error.</span></li>
<li class="c2"><span class="c11">searchReposLiveData = MutableLiveData&lt;List&lt;GithubRepoDomain&gt;&gt;()</span><span class="c0">. Este LiveData proporcionar&aacute; los datos a la UI. Nuestra vista debe suscribirse en &eacute;l para obtener el listado de elementos una vez sean cargados.</span></li>
</ul>
<p class="c5 c12"> </p>
<p class="c5"><span class="c0">Tendr&aacute; un m&eacute;todo que consultar&aacute; al repositorio los datos y se encargar&aacute; de notificar a la vista. Este m&eacute;todo lo ver&aacute;s en el apartado de &ldquo;Actualizamos los datos&rdquo;.</span></p>
<h2 id="h.kq9zp8da5o85" class="c1"><span class="c3">Registramos la UI al ViewModel</span></h2>
<p class="c5"><span class="c0">Una vez ya tienes creado el ViewModel, tienes que registrar tu vista a sus LiveData para estar informado cuando haya un cambio en ellos. Para ello debes:</span></p>
<ul class="c4 lst-kix_y19x49lbmpr5-0 start">
<li class="c2"><span class="c0">Crear la variable viewModel como un lateinit.</span></li>
</ul>
<p class="c5"><img src="http://betabeers.com/uploads/blog/20190208_image_4.png" alt="" width="400" height="31" /></p>
<ul class="c4 lst-kix_y19x49lbmpr5-0">
<li class="c2"><span class="c0">Inicializar el ViewModel haciendo uso de ViewModelProviders.</span></li>
</ul>
<p class="c5"><img src="http://betabeers.com/uploads/blog/20190208_image_5.png" alt="" width="400" height="46" /></p>
<ul class="c4 lst-kix_y19x49lbmpr5-0">
<li class="c2"><span class="c0">Registrarse como observador en todos los LiveData que necesites.</span></li>
</ul>
<p class="c5"><img src="http://betabeers.com/uploads/blog/20190208_image_6.png" alt="" width="700" height="107" /></p>
<p class="c5 c12"> </p>
<p class="c5"><span>El c&oacute;digo lo puedes encontrar </span><span class="c6"><a class="c7" href="https://www.google.com/url?q=https://github.com/franlopjur/ArchitectureComponentsSample/blob/master/app/src/main/java/com/betabeers/architecturecomponentsexample/ui/activity/MainActivity.kt&amp;sa=D&amp;ust=1549588881150000">aqu&iacute;</a></span><span class="c0">.</span></p>
<h2 id="h.p26d4010e6aa" class="c1"><span class="c3">Actualizamos los datos</span></h2>
<p class="c5"><span class="c0">En el Repositorio tendr&aacute;s la siguiente implementaci&oacute;n:</span></p>
<p class="c5"><img src="http://betabeers.com/uploads/blog/20190208_image_7.png" alt="" width="700" height="283" /></p>
<p class="c5 c12"> </p>
<p class="c5"><span class="c0">Una vez la llamada haya ido bien o mal, simplemente debes pasar los datos obtenidos de la llamada a la funci&oacute;n pasada por par&aacute;metros. Estos datos ser&aacute;n obtenidos por el ViewModel y &eacute;ste se encargar&aacute; de publicarlos en los LiveData del ViewModel con el m&eacute;todo &ldquo;postValue&rdquo;. Este m&eacute;todo del ViewModel quedar&iacute;a as&iacute;:</span></p>
<p class="c5 c12"> </p>
<p class="c5"><img src="http://betabeers.com/uploads/blog/20190208_image_8.png" alt="" width="350" height="179" /></p>
<p class="c5 c12"> </p>
<p class="c5"><span>Adem&aacute;s, el c&oacute;digo de esta clase lo puedes encontrar </span><span class="c6"><a class="c7" href="https://www.google.com/url?q=https://github.com/franlopjur/ArchitectureComponentsSample/blob/master/app/src/main/java/com/betabeers/architecturecomponentsexample/ui/viewmodel/SearchRepoViewModel.kt&amp;sa=D&amp;ust=1549588881151000">aqu&iacute;</a></span><span> y el del proyecto bajo el siguiente </span><span class="c6"><a class="c7" href="https://www.google.com/url?q=https://github.com/franlopjur/ArchitectureComponentsSample&amp;sa=D&amp;ust=1549588881151000">enlace</a></span><span class="c0">.</span></p>
<p class="c5 c12"> </p>
<p class="c5"><span>En resumen, los nuevos componentes mejoran los problemas de ciclos de vida que nos encontr&aacute;bamos en MVP y aportan una arquitectura m&aacute;s robusta y adaptada a los ciclos de vida de Android. En siguientes post mejorar&aacute;s la implementaci&oacute;n realizada con los m&eacute;todos de la clase </span><span class="c6"><a class="c7" href="https://www.google.com/url?q=https://developer.android.com/reference/android/arch/lifecycle/Transformations&amp;sa=D&amp;ust=1549588881152000">Transformations </a></span><span class="c0">y avanzar&aacute;s en la arquitectura MVVM propuesta por Google.</span></p>]]></content:encoded>
</item><item>
<title><![CDATA[Desarrollo de temas WordPress con Underscores]]></title>
<link>https://betabeers.com/blog/desarrollo-temas-wordpress-underscores-391/</link>
<pubDate>Fri, 01 Feb 2019 01:00:07 +0000</pubDate>
<category>Colaboraciones</category>
<content:encoded><![CDATA[<p>Cuando hablamos de <strong>desarrollo de proyectos web con WordPress</strong> en muchas ocasiones nos referimos a la creaci&oacute;n de temas personalizados desde cero.</p>
<p>En anteriores ediciones he tenido la ocasi&oacute;n de hablar de algunos <a href="../starter-themes-wordpress-355/" target="_blank">starter themes para WordPress</a> que nos permiten disponer de una estructura o framework para poder trabajar de una forma &aacute;gil en un nuevo tema.</p>
<p><img style="display: block; margin-left: auto; margin-right: auto;" title="Desarrollo de temas WordPress con Underscores" src="http://betabeers.com/uploads/blog/20190201_desarrollo-temas-wordpress-underscores.jpg" alt="Desarrollo de temas WordPress con Underscores" width="500" height="333" /></p>
<p>En esta ocasi&oacute;n contin&uacute;o en esa l&iacute;nea, pero centr&aacute;ndome en <strong>Underscores</strong>, un tema pensado para desarrolladores que respeta la forma de funcionar de WordPress del mejor modo y que deber&iacute;a ser nuestra referencia a la hora de trabajar en nuestros dise&ntilde;os web.</p>
<h2>&iquest;Qu&eacute; es Underscores?</h2>
<p><a href="https://underscores.me/" target="_blank">Underscores ( _s)</a> es un <em>starter theme </em>o tema creado para trabajar desde cero en nuestros proyectos WordPress. Est&aacute; creado por el equipo de desarrolladores de Automattic, su c&oacute;digo es libre, y est&aacute; sujeto a modificaciones a trav&eacute;s del <a href="https://github.com/automattic/_s" target="_blank">repositorio p&uacute;blico de Github</a>.</p>
<p>Con Underscores podemos disponer de un <strong>tema base sobre el cual trabajar</strong> a&ntilde;adiendo nuestros propios estilos CSS y completando las funciones y plantillas espec&iacute;ficas del propio tema. No hablamos en este caso de un tema sobre el cual crear otro tema hijo, sino de un tema que en s&iacute; mismo no recibir&aacute; actualizaciones al ser creado y desarrollado desde cero por nosotros.</p>
<p>Se caracteriza por respetar el <strong>modo de codificaci&oacute;n recomendado por la documentaci&oacute;n de WordPress</strong> a la hora de jerarquizar archivos, disponer de una hoja de estilos simple y bien estructurada sobre la cual poder trabajar y estar ampliamente mantenido por la comunidad con constantes mejoras y actualizaciones.</p>
<p>Desde el propio sitio web de Underscores es posible descargar una copia personalizada que directamente podemos utilizar y sobre la que podremos trabajar en nuestros desarrollos.</p>
<h2>Ventajas de utilizar Underscores</h2>
<p>&iquest;Por qu&eacute; te recomiendo conocer y utilizar Underscores en tus temas WordPress creados desde cero? Adem&aacute;s de las razones indicadas anteriormente, me gustar&iacute;a enumerar unas cuantas razones que har&aacute;n que desees comenzar a probarlo:</p>
<ul>
<li>Te permitir&aacute; conocer, de una manera sencilla, el funcionamiento de la jerarqu&iacute;a de archivos de un tema WordPress.</li>
<li>Ofrece un c&oacute;digo limpio, sem&aacute;ntico y bien comentado.</li>
<li>Incluye solamente los elementos necesarios para que el sitio web funcione de una manera optimizada y accesible.</li>
<li>Dispondr&aacute;s de una hoja de estilos simple y estructurada incluyendo simplemente elementos de reajuste y normalizaci&oacute;n.</li>
<li>Incluye soporte para men&uacute;s en dispositivos m&oacute;viles.</li>
<li>Est&aacute; especialmente pensado para desarrolladores, siendo la base de los nuevos temas oficiales de WordPress, los famosos <em>twenties</em>.</li>
<li>Est&aacute; preparado para sitios web multiidioma y ser compatible con los plugins m&aacute;s populares como Jetpack o WooCommerce.</li>
<li>Podr&aacute;s trabajar sin problemas en la edici&oacute;n de contenidos con el nuevo editor de bloques Gutenberg.</li>
<li>Tiene el tiempo suficiente de desarrollo y uso dentro de la comunidad como para ser considerado el principal est&aacute;ndar a la hora de crear temas personalizados.</li>
<li>La licencia es GPL para que puedas reutilizarlo del modo en que creas conveniente.</li>
</ul>
<h2>Entendiendo los archivos de Underscores</h2>
<p>Para poder comenzar a trabajar correctamente con Underscores siempre viene bien una primera aproximaci&oacute;n a los archivos que nos vamos a encontrar una vez descargamos el tema. Haremos un breve repaso para sentar unas primeras bases.</p>
<h3>Elementos estructurales</h3>
<p><strong>header.php</strong></p>
<p>A&ntilde;ade los elementos estructurales y de declaraci&oacute;n del documento HTML finalizando el archivo con el inicio de la clase &ldquo;site-content&rdquo; para abrir el contenido. Incluye elementos de branding y el men&uacute; de navegaci&oacute;n.</p>
<p><a href="https://github.com/Automattic/_s/blob/master/header.php" target="_blank">https://github.com/Automattic/_s/blob/master/header.php</a></p>
<p><strong>footer.php</strong></p>
<p>Es un pi&eacute; de p&aacute;gina sencillo que incluye simplemente los cr&eacute;ditos de WordPress y el cierre del cuerpo del documento. Podremos ampliar su uso a&ntilde;adiendo zonas de widgets para crear bloques de contenido.</p>
<p><a href="https://github.com/Automattic/_s/blob/master/footer.php" target="_blank">https://github.com/Automattic/_s/blob/master/footer.php</a></p>
<p><strong>sidebar.php</strong></p>
<p>Incluye la llamada a una barra lateral por defecto en el caso de que exista y la opci&oacute;n de mostrar elementos en caso contrario.</p>
<p><a href="https://github.com/Automattic/_s/blob/master/sidebar.php" target="_blank">https://github.com/Automattic/_s/blob/master/sidebar.php</a></p>
<h3>Elementos de contenido</h3>
<p><strong>single.php</strong></p>
<p>A&ntilde;ade el loop de WordPress mostrando el contenido del post en el que se encuentra con el marcado de clases correspondiente.</p>
<p>Deberemos tener en cuenta que para el propio contenido se hace referencia al archivo /template-partes/content.php</p>
<p><a href="https://github.com/Automattic/_s/blob/master/header.php" target="_blank">https://github.com/Automattic/_s/blob/master/header.php</a></p>
<p><a href="https://github.com/Automattic/_s/blob/master/template-parts/content.php" target="_blank">https://github.com/Automattic/_s/blob/master/template-parts/content.php</a></p>
<p><strong>page.php</strong></p>
<p>Del mismo modo que el archivo single.php trabaja mostrando las entradas, page.php hace lo propio con las p&aacute;ginas. Tambi&eacute;n hace una llamada a /template-parts/content-page.php para el contenido en s&iacute; mismo.</p>
<p><a href="https://github.com/Automattic/_s/blob/master/page.php" target="_blank">https://github.com/Automattic/_s/blob/master/page.php</a></p>
<p><a href="https://github.com/Automattic/_s/blob/master/template-parts/content-page.php" target="_blank">https://github.com/Automattic/_s/blob/master/template-parts/content-page.php</a></p>
<p><strong>archive.php</strong></p>
<p>Podemos personalizar la apariencia de la p&aacute;gina de listado de archivos de categor&iacute;as, taxonom&iacute;as y etiquetas.</p>
<p><a href="https://github.com/Automattic/_s/blob/master/archive.php" target="_blank">https://github.com/Automattic/_s/blob/master/archive.php</a></p>
<p><strong>search.php</strong></p>
<p>Los resultados de b&uacute;squeda de WordPress se imprimen utilizando este tipo de listado de contenido.</p>
<p><a href="https://github.com/Automattic/_s/blob/master/search.php" target="_blank">https://github.com/Automattic/_s/blob/master/search.php</a></p>
<p><strong>404.php</strong></p>
<p>Underscores incluye tambi&eacute;n una p&aacute;gina de error 404 que podemos personalizar como creamos conveniente, algo no del todo habitual en muchos themes y que puede darnos bastante juego.</p>
<p><a href="https://github.com/Automattic/_s/blob/master/404.php" target="_blank">https://github.com/Automattic/_s/blob/master/404.php</a></p>
<h3>Plantilla por defecto</h3>
<p><strong>index.php</strong></p>
<p>Si alguno de los tipos de contenidos y archivos comentados anteriormente no dispone de una plantilla asignada, entra en juego el fichero &iacute;ndice que permite que se muestre la informaci&oacute;n igualmente a trav&eacute;s del loop de WordPress.</p>
<p><a href="https://github.com/Automattic/_s/blob/master/index.php" target="_blank">https://github.com/Automattic/_s/blob/master/index.php</a></p>
<h3>Comentarios</h3>
<p><strong>comments.php</strong></p>
<p>Una de las partes m&aacute;s complicadas para la maquetaci&oacute;n suele ser la secci&oacute;n de comentarios que en este caso viene incorporada con una declaraci&oacute;n de estilos sencilla y las funciones m&aacute;s habituales como navegaci&oacute;n por comentarios y comentarios anidados.</p>
<p><a href="https://github.com/Automattic/_s/blob/master/comments.php" target="_blank">https://github.com/Automattic/_s/blob/master/comments.php</a></p>
<h3>Archivo de funciones</h3>
<p><strong>functions.php</strong></p>
<p>El archivo reservado a a&ntilde;adir funcionalidades en WordPress tambi&eacute;n tiene su lugar en Underscores. En este caso tenemos elementos como el encolado de estilos y scripts, la declaraci&oacute;n de las zonas de widgets o la llamada a otros archivos de funciones de compatibilidad con Jetpack o WooCommerce</p>
<p><a href="https://github.com/Automattic/_s/blob/master/functions.php" target="_blank">https://github.com/Automattic/_s/blob/master/functions.php</a></p>
<h3>Hoja de estilos</h3>
<p><strong>style.css</strong></p>
<p>El archivo style.css ofrece un primer nivel de normalizaci&oacute;n para los estilos para despu&eacute;s incluir los elementos b&aacute;sicos de tipograf&iacute;a, elementos, navegaci&oacute;n o contenidos.</p>
<p>Dispondremos solamente de elementos b&aacute;sicos que tendremos que trabajar a posterori, pero que nos permitir&aacute;n incluir solamente aquello que sea estrictamente necesario para el proyecto.</p>
<p><a href="https://github.com/Automattic/_s/blob/master/style.css" target="_blank">https://github.com/Automattic/_s/blob/master/style.css</a></p>
<h3>Directorios de organizaci&oacute;n</h3>
<p>Adem&aacute;s de los archivos esenciales para que el contenido pueda mostrarse y dotar de funcionalidades a nuestro tema WordPress, encontramos directorios de organizaci&oacute;n que a trav&eacute;s de est&aacute;ndares nos permiten organizar la informaci&oacute;n adicional que vayamos creando siguiendo el esquema propuesto a continuaci&oacute;n.</p>
<p><strong>/inc</strong></p>
<p>Se incluyen archivos PHP que no son esenciales para el funcionamiento pero que nos permiten organizar modularmente la informaci&oacute;n que ofrecemos en el front.</p>
<p><strong>/js</strong></p>
<p>Est&aacute; pensado para a&ntilde;adir los archivos JavaScript con los que vayamos a trabajar incluyendo por defecto los siguientes: customizer.js, navigation.js y skip-link-focus-fix.js.</p>
<p><strong>/languages</strong></p>
<p>El directorio que debemos utilizar para trabajar en la localizaci&oacute;n de nuestro tema a&ntilde;adiendo aqu&iacute; los archivos de traducci&oacute;n.</p>
<p><strong>/layouts</strong></p>
<p>Permite a&ntilde;adir estilos concretos para disposiciones de estructuras diferentes a la b&aacute;sica a&ntilde;adiendo por defecto dos de las m&aacute;s utilizadas como son content-sidebar.css y sidebar-content.css.</p>
<h2>Conclusiones</h2>
<p>Si estamos pensando dedicarnos al desarrollo de temas para WordPress, encontraremos en Undescores un aliado perfecto para poder conocer todos los elementos necesarios para poner en marcha un nuevo theme limpio, ordenado y optimizado. Incluso podemos avanzar en la creaci&oacute;n de nuestro propio entorno de trabajo utilizando alternativas avanzadas como <a title="Understrap" href="https://understrap.com/" target="_blank">Understrap</a> o algunas otras que podemos encontrar en Github tales como <a href="https://github.com/teamscops/bulmapress" target="_blank">bulmapress</a> o <a href="https://github.com/WebDevStudios/wd_s" target="_blank">wd_s</a>.</p>
<p>Como la mejor forma de aprender es la pr&aacute;ctica, mi recomendaci&oacute;n personal es que descargues e instales ya mismo tu propia copia de Underscores y te pongas manos a la obra a&ntilde;adiendo tus propias modificaciones en la hoja de estilos y nuevas funciones para comprobar los excelentes resultados que puedes conseguir.</p>]]></content:encoded>
</item><item>
<title><![CDATA[Angular para principiantes]]></title>
<link>https://betabeers.com/blog/angular-principiantes-390/</link>
<pubDate>Fri, 25 Jan 2019 10:38:49 +0000</pubDate>
<category>Colaboraciones</category>
<content:encoded><![CDATA[<p><em>Este art&iacute;culo ha sido escrito por <a href="https://twitter.com/PuripuriGRome?lang=es" target="_blank">Puri Garc&iacute;a Romeralo</a> y publicado originalmente en el blog de <a href="https://solidgeargroup.com/angular-para-principiantes?lang=es" target="_blank">Solid GEAR</a>.</em></p>
<h1 class="entry-title" style="text-align: center;"><img title="Angular para principiantes" src="http://betabeers.com/uploads/blog/20190125_AngularPrincipiantes.jpg" alt="Angular para principiantes" width="770" height="334" /></h1>
<h1 class="entry-title">Angular para principiantes</h1>
<p>En Solid Gear, nos gusta aprender y estar al d&iacute;a. Para tal fin, la empresa nos ofrece unos d&iacute;as de formaci&oacute;n en la que puedas explorar la tecnolog&iacute;a que m&aacute;s te apetezca.<br />Yo en mi caso, me decant&eacute; por <a href="https://angular.io/" rel="noreferrer noopener" target="_blank">Angular</a>. Por lo que, en este art&iacute;culo, Angular para principiantes, os voy a contar algo de lo que he aprendido.</p>
<h2>Pero, &iquest;c&oacute;mo se llama el framework exactamente?</h2>
<p>Lo primero que me llam&oacute; la atenci&oacute;n es el l&iacute;o de nombres que hay. Los expertos lo sabr&eacute;is, pero como principiante me result&oacute; curioso.</p>
<p>Para empezar, se desarroll&oacute; Angular 1, al cual tambi&eacute;n se le llama Angular JS. Sin embargo, para la versi&oacute;n 2, reescribieron todo el framework. Y es la versi&oacute;n 2 lo que se conoce como Angular. Y luego Angular 4, 5, 6 son versiones de Angular 2.</p>
<h2>&iquest;Cuando se debe usar Angular?</h2>
<p>Angular es muy &uacute;til para crear <a href="https://es.wikipedia.org/wiki/Single-page_application" rel="noreferrer noopener" target="_blank">Single Page Applications</a> (SPA). En este tipo de p&aacute;ginas web se descargan todo el c&oacute;digo que se necesita, y luego van mostrando las diferentes secciones usando javaScript. Por lo tanto, no se vuelve a hacer peticiones al servidor como en las p&aacute;ginas web cl&aacute;sicas al navegar ella.</p>
<p> </p>
<h2>&iquest;Qu&eacute; he aprendido por ahora?</h2>
<p>Los conceptos b&aacute;sicos que se deben conocer para usar Angular son las componentes, las directivas y los modelos.</p>
<p>Como resultado de mi formaci&oacute;n, en este art&iacute;culo, vamos a hablar sobre las componentes y la comunicaci&oacute;n entre ellas.</p>
<h3>Componentes</h3>
<p>En Angular se trabaja con componentes. Las componentes tienen una plantilla .html, un fichero typeScript y opcionalmente una plantilla .css.</p>
<p>Esto permite aislar la l&oacute;gica y los estilos de esa componente, y adem&aacute;s hacerla reusable. Todo muy interesante.</p>
<p>Ejemplo:</p>
<pre class="wp-block-code"><code>import { Component, OnInit, Input } from '@angular/core';

@Component({
  selector: 'app-first',
  templateUrl: './first.component.html',
  styleUrls: ['./first.component.css']
})
export class FirstComponent implements OnInit {

  constructor() { }

  ngOnInit() {
  }

}
</code></pre>
<p>As&iacute; se a&ntilde;ade la componente en la plantilla HTML de otra: </p>
<pre class="wp-block-code"><code>&lt;app-first&gt;&lt;/app-first&gt;</code></pre>
<p>Llamaremos componente hija a app-first y componente padre a la componente donde hemos puesto ese c&oacute;digo.</p>
<h3>Databinding</h3>
<p>Vamos a ver la comunicaci&oacute;n dentro de la componente, las diferentes formas de transmitir informaci&oacute;n que existen entre javaScript (l&oacute;gica de negocio) y la plantilla (html):</p>
<ul>
<li>string interpolation
<p>Las variables declaradas en la clase en el fichero .ts :</p>
<pre class="wp-block-code"><code>export class FirstComponent {
   elementId: number = 10;
}</code></pre>
<p>Para mostrar ese valor por la pantalla, se debe poner este c&oacute;digo en la plantilla html:</p>
<pre class="wp-block-code"><code>{{elementId}}</code></pre>
<p> </p>
</li>
</ul>
<ul>
<li>Property binding: [property]=&rdquo;data&rdquo;.<br />
<p>De este modo, damos un valor a la propiedad disabled a trav&eacute;s de una variable javaScript llamada isDisabled declarada en el fichero .ts de la componente.</p>
</li>
</ul>
<pre class="wp-block-code"><code>&lt;button class="btn btn-primary" [disabled]=&rdquo;isDisabled&rdquo;&gt;Add Server&lt;/button&gt;<br /><br /></code></pre>
<ul>
<li>Event binding: (event)=&rdquo;expression&rdquo;<br />
<p>En la plantilla html de la componente se pone </p>
<pre class="wp-block-code"><code>&lt;button (click)=&rdquo;onAddNewElement()&rdquo;&gt;&lt;/button&gt;</code></pre>
<p>Entonces, en el fichero .ts:</p>
<pre class="wp-block-code"><code>onAddNewElement(){

}</code></pre>
<p>De este modo, cuando se hace click en el bot&oacute;n, se llama a la funci&oacute;n.</p>
</li>
</ul>
<ul>
<li>two-way-binding: [(ngModel)]=&rdquo;data&rdquo;<br />
<p>Se combina property binding y event binding, por eso usa corchetes y par&eacute;ntesis.</p>
<pre class="wp-block-code"><code> &lt;input type="text"  class="form-control"  [(ngModel)]="elementName"&gt;</code></pre>
<p>Esto hace que cuando se lanza el evento input y se actualiza el valor del elementName en nuestra componente autom&aacute;ticamente. Pero adem&aacute;s (como es two-way-binding) se actualizar&aacute; el valor del input si lo modificamos en otro sitio.</p>
</li>
</ul>
<p> </p>
<h3>Comunicaci&oacute;n entre componentes</h3>
<ul>
<li>Binding to custom property.<br />
<p>En esta secci&oacute;n vamos a ver c&oacute;mo transmitir informaci&oacute;n de una componente padre a una hija.</p>
<p>Por defecto las propiedades de una componente solo son accesibles desde la propia componente. Tenemos la propiedad elementName, declarada en la clase en el fichero .ts de la componente hija.</p>
<pre class="wp-block-code"><code>elementName: {type: string, name: string, content: string}</code></pre>
<p>Por ese motivo, si queremos permitir a las componentes padres acceder a esa propiedad, necesitamos a&ntilde;adir un decorador (y importar el modulo input en esa clase).</p>
<pre class="wp-block-code"><code>@Input() elementName: {type: string, name: string, content: string}</code></pre>
<p>En el fichero .html de la componente padre, entonces podemos poner: </p>
<pre class="wp-block-code"><code>&lt;app-first *ngFor=&rdquo;let element of elementList&rdquo; [elementName]=&rdquo;element&rdquo;&gt;&lt;/app-first&gt;</code></pre>
<p>Sin embargo, si no quisi&eacute;semos usar el mismo nombre fuera que dentro, podr&iacute;amos hacerlo as&iacute;:</p>
<pre class="wp-block-code"><code>@Input(&lsquo;otherElementName&rsquo;) elementName: {type: string, name: string, content: string}</code></pre>
<p>Y en la componente padre:</p>
</li>
</ul>
<pre class="wp-block-code"><code>&lt;app-first *ngFor=&rdquo;let element of elementList&rdquo; [otherElementName]=&rdquo;element&rdquo;&gt;&lt;/app-first&gt;</code></pre>
<ul>
<li>Binding to custom events<br />
<p>En esta otra secci&oacute;n, vamos a ver c&oacute;mo transmitir informaci&oacute;n de una componente hija a la padre.</p>
<p>Por ejemplo, creamos un nuevo elemento en la componente hija y queremos informar al padre de c&oacute;mo se llama.</p>
<p>En la plantilla de la componente hija (app-first), tenemos este c&oacute;digo </p>
<pre class="wp-block-code"><code>&lt;button (click)=&rdquo;onAddNewElement()&rdquo;&gt;&lt;/button&gt;</code></pre>
<p>Cuando creamos el elemento, queremos transmitirle el nombre a la padre. Para ello, en el fichero .ts de la componente hija necesitamos crear un evento para transmitir esa informaci&oacute;n.</p>
<p>Creamos una propiedad llamada <strong>elementCreated </strong>del tipo EventEmitter, indicando los par&aacute;metros que se van a emitir. Adem&aacute;s hay que a&ntilde;adirle el decorador Output para que el evento se puede escuchar desde fuera.</p>
<pre class="wp-block-code"><code>@Output() elementCreated = new EventEmitter();</code></pre>
<p>Cuando se hace click en el bot&oacute;n para crear un nuevo elemento, se llama a la funci&oacute;n onAddNewElement que es la que va emitir el evento:</p>
<pre class="wp-block-code"><code>onAddNewElement() {
this.elementCreated.emit(this.newElementName);
}</code></pre>
<p>Entonces en la plantilla .html de la componente padre, a&ntilde;adimos el c&oacute;digo correspondiente para escuchar ese evento:</p>
<pre class="wp-block-code"><code>&lt;app-first (elementCreated)=&rdquo;onElementAdded($event)&rdquo;&lt;/app-first&gt;</code> </pre>
<p>En el archivo .ts de la componente padre, se declararia la funci&oacute;n onElementAdded.</p>
<pre class="wp-block-code"><code>onElementAdded(elementName: string){

}</code></pre>
</li>
</ul>
<p> </p>
<p>&iquest;Qu&eacute; es lo que m&aacute;s te llam&oacute; la atenci&oacute;n a ti cuando empezaste a aprender Angular?</p>]]></content:encoded>
</item><item>
<title><![CDATA[Secure Angularjs. Que no te vulneren el JS.]]></title>
<link>https://betabeers.com/blog/secure-angularjs-que-no-te-vulneren-el-js-389/</link>
<pubDate>Fri, 18 Jan 2019 10:15:33 +0000</pubDate>
<category>Colaboraciones</category>
<content:encoded><![CDATA[<p><span id="docs-internal-guid-7761792a-7fff-07a1-d789-1eb5b6b452ed"><span><img style="display: block; margin-left: auto; margin-right: auto;" src="https://lh3.googleusercontent.com/BVj56kZHzljBLZZpnEjzALyzjbGS57pmb47hFiBylv93Soa4RGnNXQfXtiQG4IWghSsyLqebc6xF6mWXVtLirrWQJTtP3Qn0xO-eRjPcW_tiNdkERGhR1-6AY-gwGEQRWTZs3vay" alt="" width="602" height="339" /></span></span></p>
<p> </p>
<p dir="ltr"><strong>Como ya sabemos Angular es un framework de javaScript de c&oacute;digo abierto utilizado para facilitar la creaci&oacute;n de aplicaciones WEB que incluyen Modelo Vista Controlador, para que su desarrollo sea sencillo. Pero como todas las tecnolog&iacute;as no est&aacute; exenta de poder ser atacada y con lo cual debemos de llevar una metodolog&iacute;a de buenas pr&aacute;cticas en seguridad para minimizar los fallos en vuestras aplicaciones. Estas buenas pr&aacute;cticas ser&aacute;n las siguientes.</strong></p>
<p> </p>
<p dir="ltr"><strong>Utiliza la versi&oacute;n m&aacute;s reciente de AngularJS.</strong></p>
<p> </p>
<p dir="ltr"><span>Como en todas las tecnolog&iacute;as que utilicemos para crear nuestro c&oacute;digo, ya sea el entorno de desarrollo o cualquier otro framework, es siempre necesario utilizar la versi&oacute;n m&aacute;s reciente, puesto que tendr&aacute; corregidos los fallos y vulnerabilidades encontrados hasta el momento. Tambi&eacute;n debemos estar a dia de nuevos parches de seguridad que puedan aparecer para tenerlos aplicados en nuestros desarrollos.</span></p>
<p> </p>
<p dir="ltr"><strong>Ce&ntilde;irse a expresiones y plantillas propias de AngularJS.</strong></p>
<p> </p>
<p dir="ltr"><span>Si no somos escrupulosos a la hora de escribir nuestro c&oacute;digo con las plantillas y expresiones de angular un atacante podr&iacute;a explotar un ataque del tipo XSS (cross-site scripting) para controlar nuestra aplicaci&oacute;n.</span></p>
<p dir="ltr"><span>Podemos utilizar plantillas del lado del servidor que est&eacute;n adecuadamente verificadas para generar contenido din&aacute;mico, pero no para generar plantillas generadas por Angularjs. Una muy buena opci&oacute;n es dise&ntilde;ar nuestra aplicaci&oacute;n de tal manera que los usuarios no puedan cambiar las plantillas del lado del cliente.</span></p>
<p> </p>
<p dir="ltr"><strong>Solicitudes HTTP.</strong></p>
<p> </p>
<p dir="ltr"><span>Siempre que una aplicaci&oacute;n realiza solicitudes a un servidor existen posibles problemas de seguridad que deben bloquearse, servidor y cliente deben actuar para eliminar estas amenazas. Aunque Angularjs viene preconfigurado para eliminar estas amenazas debemos configurar el servidor backend para que ayude en esta labor.</span></p>
<p> </p>
<p dir="ltr"><strong>Protecci&oacute;n contra XSS ( Cross-site Scripting).</strong></p>
<p> </p>
<p dir="ltr"><span><span id="docs-internal-guid-39b68de2-7fff-3d5e-515a-a7c57010a972"><span>Este tipo de ataques permiten a un ciberdelincuente inyectar c&oacute;digo malicioso en una WEB. Esta inyecci&oacute;n puede robar datos de usuarios haci&eacute;ndose pasar por un usuario leg&iacute;timo, por eso hay que procurar que dicho c&oacute;digo ingrese al DOM de la p&aacute;gina puesto que un atacante podr&iacute;a ingresar &ldquo;&lt;script&gt;&ldquo; y ejecutar esas acciones malintencionadas. Con lo cual es important&iacute;simo filtrar todo el contenido que se inserte en nuestra aplicaci&oacute;n, para lo cual Angular trata todas las entradas como no confiables bloqueando todos los valores inv&aacute;lidos,  no debemos generar c&oacute;digo fuente de la plantilla concatenando las entradas de usuario, como prevenci&oacute;n debemos utilizar el compilador de plantillas.</span></span></span></p>
<p dir="ltr"><strong><br class="Apple-interchange-newline" /><span id="docs-internal-guid-9a79ba20-7fff-b58c-6c72-77e4d96ce1db"><span>Utilizar el compilador de plantillas offline.</span></span><br /></strong></p>
<p> </p>
<p dir="ltr"><span id="docs-internal-guid-0adefab1-7fff-dee7-c72f-abae51b970cc"><span>Esta pr&aacute;ctica nos previene de una vulnerabilidad llamada inyecci&oacute;n de plantillas,  adem&aacute;s mejora mucho el rendimiento de nuestras plantillas.</span></span></p>
<p dir="ltr"> </p>
<p dir="ltr"><strong><span id="docs-internal-guid-3558939a-7fff-3115-fd40-f66670f0031e"><span>Auditar nuestras aplicaciones.</span></span></strong></p>
<p> </p>
<p dir="ltr"><span>En definitiva una aplicaci&oacute;n construida mediante Angular debe de seguir los mismos principios en materia de seguridad que cualquier  otra tecnolog&iacute;a. Utilizando los m&eacute;todos bypassSecurityTrust nos aseguraremos una capa mas de seguridad auditando los datos introducidos en ella, estos se llaman </span><span>DomSanitizer</span><span>:</span></p>
<p dir="ltr"><span><span> </span></span></p>
<ul>
<li dir="ltr">
<p dir="ltr"><span>bypassSecurityTrustHtml</span></p>
</li>
<li dir="ltr">
<p dir="ltr"><span>bypassSecurityTrustScript</span></p>
</li>
<li dir="ltr">
<p dir="ltr"><span>bypassSecurityTrustStyle</span></p>
</li>
<li dir="ltr">
<p dir="ltr"><span>bypassSecurityTrustUrl</span></p>
</li>
<li dir="ltr">
<p dir="ltr"><span>bypassSecurityTrustResourceUrl</span></p>
</li>
</ul>
<p dir="ltr"><strong><br class="Apple-interchange-newline" /><span id="docs-internal-guid-3e95837c-7fff-db88-9ced-d61285e6de27"><span>Utilizar la intercepci&oacute;n de respuestas.</span></span><br /></strong></p>
<p> </p>
<p dir="ltr"><span>Algo muy &uacute;til a la hora de auditar nuestra aplicaci&oacute;n Angularjs es realizar una intercepci&oacute;n de respuestas con el fin de conocer el comportamiento de la misma y corregir los posibles errores que pudiera tener. Antes de presentar los datos al usuario debemos ejecutar diversas acciones como es validar los datos introducidos, disponemos de un API llamado </span><span>Response Interceptor </span><span>con la que podemos lleva  a cabo estas acciones de una manera sencilla, sin necesidad de realizar nosotros mismos el componente.</span></p>
<p dir="ltr"><span>Otra buena pr&aacute;ctica es utilizar autenticaci&oacute;n basada en token, podemos realizarlo manualmente o utilizando alguna librer&iacute;a que nos facilite la labor como por ejemplo Satellizer. De esta manera nos aseguraremos que todas las peticiones pase por el interceptor validando de esta manera la sesi&oacute;n asegur&aacute;ndonos que la petici&oacute;n es realizada por un usuario leg&iacute;timo.</span></p>
<p> </p>
<p dir="ltr"><strong>Conclusi&oacute;n.</strong></p>
<p dir="ltr"><span>Angularjs est&aacute; pensada para trabajar del lado de cliente y partiendo de la base que todo lo que se ejecute del lado del cliente es inseguro, deberemos adem&aacute;s de realizar las anteriores recomendaciones, implementar toda la seguridad posible en nuestra aplicaci&oacute;n del lado del servidor, por que de ella depender&aacute; la base de la seguridad de la aplicaci&oacute;n. La mejor seguridad que podemos darle a nuestros desarrollos es exponerla lo menos posible al lado del usuario y controlar todos los datos que &eacute;stos introduzcan.</span></p>
<p><span> </span></p>
<p><span><span id="docs-internal-guid-67a0a5fd-7fff-9a95-fdf2-31f315f58e54"><span>Para m&aacute;s informaci&oacute;n https://angular.io.</span></span></span></p>]]></content:encoded>
</item><item>
<title><![CDATA[MotionLayout: KeyCycle, KeyTimeCycle y KeyTrigger #AndroidMeetsKotlin]]></title>
<link>https://betabeers.com/blog/motionlayout-keycycle-keytimecycle-keytrigger-androidmeetskotlin-388/</link>
<pubDate>Fri, 11 Jan 2019 01:07:04 +0000</pubDate>
<category>Colaboraciones</category>
<content:encoded><![CDATA[<h2 id="h.sillutnyvl1a" class="c12"><img style="display: block; margin-left: auto; margin-right: auto;" src="http://betabeers.com/uploads/blog/20190111_image_1.png" alt="" width="550" height="269" /></h2>
<p class="c1"> </p>
<p class="c7">Google ha hecho los deberes antes de irse de vacaciones y ha sacado el 21 de Diciembre de 2018 la versi&oacute;n &ldquo;alpha3&rdquo; de ConstraintLayout v2.0.0 aportando nuevos componentes como son: KeyCycle, KeyTimeCycle y KeyTrigger (entre otros). En los anteriores post sobre MotionLayout (<a class="c11" href="https://www.google.com/url?q=https://betabeers.com/blog/motionlayout-motionscene-la-simpleza-las-animaciones-cada-vez-mas-cerca-androidmeetskotlin-380/&amp;sa=D&amp;ust=1547167742293000">parte 1</a> y <a class="c11" href="https://www.google.com/url?q=https://betabeers.com/blog/motionlayout-customattribute-keyframeset-keyposition-androidmeetskotlin-384/&amp;sa=D&amp;ust=1547167742293000">parte 2</a>) has podido ver pr&aacute;cticamente todos los elementos que estaban en la versi&oacute;n &ldquo;constraint-layout:2.0.0-alpha2&rdquo; de este componente.</p>
<p class="c7">Puesto que est&aacute; en continua expansi&oacute;n, en este post vas a ver nuevos elementos que dotar&aacute;n a tus animaciones de una mayor personalizaci&oacute;n y control. Sin m&aacute;s, &iexcl;manos a la obra!</p>
<p class="c7"><img src="http://betabeers.com/uploads/blog/20190111_image_2.png" alt="" width="600" height="23" /></p>
<h3 id="h.iojfgayzeyat" class="c6">Cambios a la hora de definir los ConstraintSet en los MotionScene</h3>
<p class="c7">Esta nueva release trae una ventaja muy importante, adi&oacute;s al c&oacute;digo repetido entre los ConstraintSet de inicio y fin. Es decir, en la versi&oacute;n &ldquo;alpha2&rdquo; los elementos &ldquo;Constraint&rdquo; ten&iacute;an que contener TODOS los atributos definidos en una vista ya que ser&iacute;an todos reemplazados cuando se aplicaba las de la animaci&oacute;n de fin. Ahora, s&oacute;lo bastar&iacute;a con agregar aquellas &aacute;reas o elementos espec&iacute;ficos que se van a modificar, por tanto, podemos a&ntilde;adirlas dentro del tag &ldquo;Constraint&rdquo; como:</p>
<ul class="c4 lst-kix_tlowpkqykiyt-0 start">
<li class="c3">&ldquo;&lt;<strong>Layout</strong>&gt;&rdquo;. Se trata de todas las constraints para las vistas.</li>
<li class="c3">&ldquo;&lt;<strong>PropertySet</strong>&gt;&rdquo;. Cambios de propiedades como visibilidad, alfa y progreso.</li>
<li class="c3">&ldquo;&lt;<strong>Transform</strong>&gt;&rdquo;. Cambios de escala, traslaciones, rotaciones, pivotes, elevaciones, etc.</li>
<li class="c3">&ldquo;&lt;<strong>Motion</strong>&gt;&rdquo;: formas, arcos, etc.</li>
</ul>
<p class="c1"> </p>
<p class="c7">Para que lo entiendas mejor, vamos a ver el cambio que supone esto en las ConstraintSet de fin para la vista &ldquo;b&rdquo; que ten&iacute;amos en la <a class="c11" href="https://www.google.com/url?q=https://github.com/franlopjur/MotionLayout-parte-2-/blob/master/animation_with_custom_attributes.xml&amp;sa=D&amp;ust=1547167742296000">parte 2</a> de esta sucesi&oacute;n de posts.</p>
<p class="c1"> </p>
<p class="c7"><img src="http://betabeers.com/uploads/blog/20190111_image_3.png" alt="" width="700" height="168" /></p>
<p class="c1"> </p>
<p class="c7">Tienes todo el c&oacute;digo de este layout de forma simplificada en el siguiente <a class="c11" href="https://www.google.com/url?q=https://github.com/franlopjur/MotionLayout-parte-2-/blob/master/animation_motion_scene_new_version.xml&amp;sa=D&amp;ust=1547167742297000">enlace</a>.</p>
<h3 id="h.ldetcqsefxl0" class="c6">Cambios en el tag OnClick</h3>
<ul class="c4 lst-kix_kunr94fbrxiz-0 start">
<li class="c3">Soporta m&uacute;ltiples &ldquo;OnClick&rdquo; por escena.</li>
<li class="c3">Se ha modificado <strong>motion:mode</strong> por <strong>motion:clickAction</strong>.</li>
</ul>
<h3 id="h.xxdfy296afbb" class="c6">KeyTimeCycle</h3>
<p class="c7">Se trata de un elemento que permite definir un ciclo impulsado por el tiempo en lugar de por el progreso de la transici&oacute;n.</p>
<h3 id="h.oc0otvgevt95" class="c6">KeyCycle  </h3>
<p class="c7">Su funci&oacute;n es controlar las oscilaciones del objeto que se anima. Por tanto, al usar un KeyCycle junto con los KeyPosition, puedes agregar todas las oscilaciones que necesites a la animaci&oacute;n. Su creaci&oacute;n es similar a los otros componentes que has visto, debes proporcionar nuevamente el ID del elemento de destino, una posici&oacute;n a lo largo de la l&iacute;nea de tiempo y el valor deseado de la propiedad que debe oscilar hacia adelante y hacia atr&aacute;s. Tambi&eacute;n puedes configurar un oscilador proporcionando detalles como la forma de onda a usar y el per&iacute;odo de la misma. Las propiedades son:</p>
<ul class="c4 lst-kix_4q5cxsid97so-0 start">
<li class="c3">&ldquo;<strong>target</strong>&rdquo;. Id de la vista.</li>
<li class="c3">&ldquo;<strong>framePosition</strong>&rdquo;. El punto en el que se aplicar&aacute; la oscilaci&oacute;n, desde 0 a 100.</li>
<li class="c3">Atributos de vista generales, &eacute;stos est&aacute;n definidos en el <a class="c11" href="https://www.google.com/url?q=https://developer.android.com/reference/android/support/constraint/motion/MotionLayout%23standard-attributes&amp;sa=D&amp;ust=1547167742299000">siguiente enlace</a>.</li>
<li class="c3">&ldquo;<strong>waveShape</strong>&rdquo;. La forma de la onda a generar. Valores posibles: {sin | square | triangle | sawtooth | reverseSawtooth | cos | bounce}.</li>
<li class="c3">&ldquo;<strong>wavePeriod</strong>&rdquo;. El n&uacute;mero de ciclos que se repetir&aacute; la onda.</li>
<li class="c3">&ldquo;<strong>waveOffset</strong>&rdquo;. Valor del &ldquo;offset&rdquo; que se le a&ntilde;adir&aacute; al atributo.</li>
<li class="c3">&ldquo;<strong>transitionPathRotate</strong>&rdquo;. Ciclos aplicados a la rotaci&oacute;n en relaci&oacute;n con la ruta que recorre la vista.</li>
<li class="c3">&ldquo;<strong>progress</strong>&rdquo;.Llama al m&eacute;todo &ldquo;setProgress&rdquo; de la vista.</li>
<li class="c3">Todos el listado de &lt;<strong>CustomAttribute</strong>&gt; como has visto en los otros componentes.</li>
</ul>
<p class="c1"> </p>
<p class="c7">Si tienes la animaci&oacute;n que se indica en el siguiente gif (Sin KeyCycle) y queremos a&ntilde;adirle un KeyCycle que cambie su rotaci&oacute;n en 90&ordm; mediante una onda (de tipo &ldquo;sin&rdquo;) en su instante 50, &eacute;sta quedar&iacute;a de la siguiente forma:</p>
<ul class="c4 lst-kix_vl16g0n0ljx4-0 start">
<li class="c3"><a class="c11" href="https://www.google.com/url?q=https://github.com/franlopjur/MotionLayout-parte-2-/blob/master/sin_keycycle.gif&amp;sa=D&amp;ust=1547167742301000">Animaci&oacute;n sin KeyCycle</a></li>
<li class="c3"><a class="c11" href="https://www.google.com/url?q=https://github.com/franlopjur/MotionLayout-parte-2-/blob/master/con_keycycle.gif&amp;sa=D&amp;ust=1547167742302000">Animaci&oacute;n con KeyCycle</a></li>
</ul>
<p class="c1"> </p>
<p class="c7">El c&oacute;digo que tendr&aacute;s que a&ntilde;adir ser&aacute; el siguiente:</p>
<p class="c1"> </p>
<p class="c7"><img src="http://betabeers.com/uploads/blog/20190111_image_4.png" alt="" width="300" height="138" /></p>
<p class="c1"> </p>
<h3 id="h.vekit7fbech3" class="c6">KeyTrigger</h3>
<p class="c7">A&ntilde;ade un elemento que puede activar el retorno de eventos en funci&oacute;n del progreso de la animaci&oacute;n actual. &Eacute;ste evento podr&aacute; ser capturado en el m&eacute;todo onTransitionTrigger de TransitionListener.</p>
<h3 id="h.3nuuufwlynj1" class="c6">&iexcl;Bonus! TransitionListener</h3>
<p class="c7">Adem&aacute;s, como elemento muy interesante, vas a poder conocer los eventos (callbacks) que tiene TransitionListener ya que en muchas ocasiones puedes necesitar saber cu&aacute;ndo acaba o empieza la animaci&oacute;n para realizar seguidamente otra acci&oacute;n. Los m&eacute;todos que nos encontramos son los siguientes:</p>
<ul class="c4 lst-kix_we9k3tpf2tkn-0 start">
<li class="c3"><strong>onTransitionTrigger(view: MotionLayout?, p1: Int, p2: Boolean, progress: Float)</strong>. Se lanza a ra&iacute;z de los KeyTriggers configurados en la misma.</li>
<li class="c3"><strong>onTransitionStarted(view: MotionLayout?, startId: Int, endId: Int)</strong>. Es llamado cuando comienza la animaci&oacute;n.</li>
<li class="c3"><strong>onTransitionChange(view: MotionLayout?, startId: Int, endId: Int, progress: Float)</strong>. Este evento es llamado cada vez que se produce un cambio en la animaci&oacute;n.</li>
<li class="c3"><strong>onTransitionCompleted(view: MotionLayout?, currentId: Int)</strong>. &Eacute;ste, por el contrario, se llama cuando la animaci&oacute;n se ha completado, ya sea de inicio o fin.</li>
</ul>
<p class="c1"> </p>
<p class="c7">En resumidas cuentas, la nueva versi&oacute;n de MotionLayout trae nuevas caracter&iacute;sticas que ser&aacute;n muy &uacute;tiles a la hora de realizar tus animaciones. Sin embargo, a&uacute;n te encontrar&aacute;s con poca documentaci&oacute;n de la misma y errores propios de una versi&oacute;n alpha. Puedes encontrar informaci&oacute;n de los cambios de esta nueva versi&oacute;n en el siguiente <a class="c11" href="https://www.google.com/url?q=https://androidstudio.googleblog.com/2018/12/constraintlayout-200-alpha-3.html&amp;sa=D&amp;ust=1547167742304000">enlace</a> y siempre podr&aacute;s encontrar toda la informaci&oacute;n relativa a MotionLayout en el siguiente <a class="c11" href="https://www.google.com/url?q=https://developer.android.com/reference/android/support/constraint/motion/MotionLayout&amp;sa=D&amp;ust=1547167742305000">enlace</a>.</p>]]></content:encoded>
</item><item>
<title><![CDATA[Optimizando el rendimiento de la base de datos de WordPress]]></title>
<link>https://betabeers.com/blog/optimizando-el-rendimiento-la-base-datos-wordpress-387/</link>
<pubDate>Fri, 21 Dec 2018 09:51:40 +0000</pubDate>
<category>Colaboraciones</category>
<content:encoded><![CDATA[<p>Durante los &uacute;ltimos a&ntilde;os hemos sido testigos del auge de la <strong>optimizaci&oacute;n del rendimiento de nuestros sitios web</strong> como unos pilares fundamentales a tener en cuenta en la calidad de nuestros proyectos.</p>
<p>Los buscadores como Google han premiado con un mejor posicionamiento a las webs m&aacute;s veloces haciendo que el <strong>WPO</strong> sea un apartado a tener en cuenta en <strong>nuestros desarrollos de sitios web</strong>, especialmente con WordPress.</p>
<p><img style="display: block; margin-left: auto; margin-right: auto;" title="Optimizando bases de datos WordPress" src="http://betabeers.com/uploads/blog/20181221_optimizando-rendimiento-base-datos-wordpress-betabeers.jpg" alt="Optimizando bases de datos WordPress" width="400" height="267" /></p>
<p>El camino tomado inicialmente ha pasado por la implementaci&oacute;n de <strong>sistemas de cach&eacute; a nivel de servidor y navegador</strong>, la <strong>optimizaci&oacute;n de im&aacute;genes</strong> o el <strong>procesado de recursos CSS y JS</strong> en el desarrollo de un sitio web creado con WordPress. Por otro lado, es cierto que las labores de mantenimiento y mejora a nivel de administraci&oacute;n de sistemas y bases de datos han pasado a un segundo plano, al menos para los sitios web de tama&ntilde;o peque&ntilde;o y mediano, o con un presupuesto m&aacute;s limitado.</p>
<p>En este caso, queremos adentrarnos en el apartado de las bases de datos de nuestro CMS favorito, aportando algunos consejos a tener en cuenta para mejorar el rendimiento de nuestros sitios web en el campo del <strong>almacenamiento de datos y consultas a las bases de datos</strong>, como valor a&ntilde;adido a nuestras labores como desarrolladores web.</p>
<h2>Estructura de la base de datos de WordPress</h2>
<p>Antes de comenzar conviene tener algunos detalles b&aacute;sicos como la estructura de tablas de la base de datos de WordPress y su funcionamiento. En este enlace del <a href="https://codex.wordpress.org/Database_Description">Codex de WordPress</a> vamos a conocer todos los detalles de cada una de sus doce tablas.</p>
<p>A nivel general, podemos tener en cuenta algunas buenas pr&aacute;cticas a la hora de desarollar nuestros proyectos, teniendo en cuenta que una correcta optimizaci&oacute;n parte de un buen planteamientos desde el inicio. Algunos <strong>consejos b&aacute;sicos que debemos tener en cuenta</strong> podr&iacute;an ser los siguientes.</p>
<ul>
<li>WordPress, temas y plugins actualizados</li>
<li>Desinstalaci&oacute;n y eliminaci&oacute;n de plugins que no est&aacute;n en uso</li>
<li>Eliminaci&oacute;n de contenido innecesario</li>
</ul>
<h2>Optimizaci&oacute;n de la base de datos a trav&eacute;s de phpMyAdmin</h2>
<p>A trav&eacute;s de diferentes &oacute;rdenes podemos realizar labores de mantenimiento que nos permitir&aacute;n reducir espacio, reparar errores y optimizar las base de datos de nuestras instalaciones de WordPress.</p>
<p><a href="https://www.phpmyadmin.net/">phpMyAdmin</a> es una herramienta de gesti&oacute;n de base de datos MySQL incluida en la mayor parte de paneles de control de alojamientos web y que est&aacute; a nuestro alcance para poder utilizarla.</p>
<p>Tambi&eacute;n podemos utilizar estas &oacute;rdenes desde la terminal si disponemos de acceso SSH, nos sentimos c&oacute;modos con el proceso y queremos ganar a&uacute;n m&aacute;s tiempo.</p>
<h3>Optimizaci&oacute;n de tablas</h3>
<p>Podemos optimizar tablas concretas de nuestra instalaci&oacute;n para reducir el tiempo de las consultas a publicaciones mediante el siguiente comando de SQL.</p>
<pre>OPTIMIZE TABLE 'wp_posts'
</pre>
<p>Reparar tablas</p>
<p>La informaci&oacute;n corrupta no solo puede retrasar las consultas a nuestra base de datos MySQL sino que adem&aacute;s podr&iacute;a hacer que nuestro sitio web fuera inaccesible. Es posible reparar las tablas de nuestra base de datos para evitar este tipo de errores.</p>
<pre>REPAIR TABLE 'wp_posts'
</pre>
<h3>Eliminar revisiones de publicaciones</h3>
<p>Si mantenemos activa la opci&oacute;n de guardado de revisiones de posts y otras publicaciones, su tama&ntilde;o puede volverse considerable. Es posible evitar que ese contenido interfiera en el resto de consultas ejecutando &oacute;rdenes como la siguiente.</p>
<pre>DELETE a,b,c

FROM wp_posts a

LEFT JOIN wp_term_relationships b ON ( a.ID = b.object_id)

LEFT JOIN wp_postmeta c ON ( a.ID = c.post_id )

LEFT JOIN wp_term_taxonomy d ON ( b.term_taxonomy_id = d.term_taxonomy_id)

WHERE a.post_type = 'revision'

AND d.taxonomy != 'link_category';
</pre>
<h3>Eliminar comentarios spam</h3>
<p>Otro de los aspectos fundamentales en el mantenimiento de un sitio web con WordPress es lidiar con los comentarios spam. Para eliminarlos de forma &aacute;gil podemos recurrir a esta consulta SQL.</p>
<pre>DELETE FROM wp_comments WHERE comment_approved = 'spam';
</pre>
<h3>Eliminar etiquetas no utilizadas</h3>
<p>Cuando nos enfrentamos al mantenimiento de un sitio web con bastante tiempo y publicaciones acumuladas es habitual encontrarse con etiquetas o tags que no han llegado a ser utilizadas aunque hayan sido publicadas. Es posible eliminar esa categor&iacute;zaci&oacute;n sin valor.</p>
<pre>DELETE FROM wp_terms WHERE term_id IN (SELECT term_id FROM wp_term_taxonomy WHERE count = 0 );

DELETE FROM wp_term_taxonomy WHERE term_id not IN (SELECT term_id FROM wp_terms);

DELETE FROM wp_term_relationships WHERE term_taxonomy_id not IN (SELECT term_taxonomy_id FROM wp_term_taxonomy);
</pre>
<h3>Eliminar pingbacks y trackbacks</h3>
<p>Cuando los blogs eran blogs, el uso de llamadas entre publicaciones mediante pingbacks y trackbacks estaba al orden del d&iacute;a. En la actualidad es muy probable que no queramos ni almacenar ni mostrar esa informaci&oacute;n.</p>
<pre>DELETE FROM wp_comments WHERE comment_type = 'pingback';

DELETE FROM wp_comments WHERE comment_type = 'trackback'; 
</pre>
<h2>Plugins recomendados para optimizar bases de datos WordPress</h2>
<p>Una vez que conocemos algunos conceptos b&aacute;sicos de optimizaci&oacute;n de bases de datos MySQL para nuestras instalaciones de WordPress, podemos probar tambi&eacute;n con algunos plugins que nos permitan ejecutar este tipo de &oacute;rdenes y consultas de una manera m&aacute;s sencilla a trav&eacute;s del propio panel de administraci&oacute;n.</p>
<p>Para este tipo de situaciones, podemos recomendar los siguientes plugins.</p>
<h3>WP-Optimize</h3>
<p><a href="https://es.wordpress.org/plugins/wp-optimize/">WP-Optimize</a> permite eliminar datos innecesarios de nuestras instalaciones y lanzar procesos de optimizaci&oacute;n de tablas de nuestra base de datos incluso de manera programada.</p>
<h3>Advanced Database Cleaner</h3>
<p><a href="https://es.wordpress.org/plugins/advanced-database-cleaner/">Advanced Database Cleaner</a> nos permite eliminar registros y tablas de la base de datos de todos aquellos plugins que hemos tenido istalados en alg&uacute;n momento, pero que ya no est&aacute;n en uso.</p>
<h3>Query Monitor</h3>
<p><a href="https://es.wordpress.org/plugins/query-monitor/%20">Query Monitor</a> es un plugin que nos permite monitorizar el rendimiento de cada una de nuestras p&aacute;ginas de WordPress a la vez que las estamos visitando. Nos ofrece informaci&oacute;n valiosa acerca del tiempo de carga del sitio, los l&iacute;mites de memoria de PHP utilizado y las consultas a la base de datos.</p>
<p>Como has podido comprobar hemos recopilado algunos consejos y buenas pr&aacute;cticas, las &oacute;rdenes de SQL m&aacute;s importantes a tener en cuenta en aspectos de optimizaci&oacute;n y algunos plugins WordPress fundamentales para poner a punto nuestras instalaciones. Como siempre, el mejor consejo es trabajar de manera ordenada desde el inicio del proyecto y prestar especial atenci&oacute;n a la optimizaci&oacute;n de nuestros proyectos desde el comienzo.</p>
<p> </p>]]></content:encoded>
</item><item>
<title><![CDATA[Integración de firmado manual en aplicaciones Xamarin.Forms]]></title>
<link>https://betabeers.com/blog/integracion-firmado-manual-aplicaciones-xamarinforms-386/</link>
<pubDate>Fri, 14 Dec 2018 10:42:26 +0000</pubDate>
<category>Colaboraciones</category>
<content:encoded><![CDATA[<p><em>Este art&iacute;culo ha sido escrito por <a href="https://twitter.com/jbarrosramos" target="_blank">Jose Antonio Barros</a> y publicado originalmente en el blog de <a href="https://solidgeargroup.com/integracion-de-firmado-manual-en-aplicaciones-xamarin-forms?lang=es" target="_blank">Solid GEAR</a>.</em></p>
<p> </p>
<p>&iquest;Quieres a&ntilde;adir la opci&oacute;n de poder recoger la firma de un usuario en tu propia app desarrollada en Xamarin? La mejor forma de hacerlo es utilizando el <strong><a href="https://www.nuget.org/packages/Xamarin.Controls.SignaturePad.Forms">nuget SignaturePad</a></strong>. Con este paquete se pueden capturar, guardar, exportar y mostrar una firma de una forma sencilla e intuitiva en aplicaciones Xamarin.Forms.</p>
<p><strong>Pasos a seguir</strong></p>
<p>En primer lugar, tenemos que <em>a&ntilde;adir el nuget tanto en la parte com&uacute;n (PCL) como en los proyectos de iOS y Android</em>. Instalaremos la versi&oacute;n 3.0, que es la m&aacute;s alta que hay a d&iacute;a de hoy.</p>
<p> </p>
<div class="wp-block-image"><img src="https://lh6.googleusercontent.com/ECI2UTB6TSQ33cCrvwOwxgCRgjdFEMB-7wa_i-GkQt3EE6XonvloU_C4TFegSGSFiHKu2OwwwYWnTtgKUaeG4S3gXraYWrDqNKi_ALpurzPCG30LOQlzWEhRcwP8VWMZ4yy1287l" alt="" /><br />
<div class="wp-block-image">
<p>El nuget est&aacute; desarrollado y mantenido por <a href="https://github.com/xamarin/SignaturePad">Microsoft</a>.</p>
<p> </p>
<p>Posteriormente, en nuestro proyecto ser&aacute; muy f&aacute;cil de utilizar. &Uacute;nicamente hay que <strong>incluir el espacio de la nueva librer&iacute;a</strong> para poder acceder a sus funcionalidades.</p>
<p>A partir de ese momento podremos <strong>incluir el control en la vista, tanto en xaml como program&aacute;ticamente</strong>. A continuaci&oacute;n se puede ver el c&oacute;digo para incluirlo de la primera forma.</p>
<p> </p>
<div class="wp-block-image"><img src="https://lh6.googleusercontent.com/P67GiWqaW5L7yrGtJSCrQNmvmXB6hG5KzPhDMvwNamDuBEupgWjhSBXwpF-PkOOoIwC3pSjt12XDAQfXtMIIbhvOuK_VSckS3-ZsDSE4oTgdI0HybG5FquR0PLs9lSJxtxNPuBWE" alt="" /><br />
<p><br />Podemos definir diferentes par&aacute;metros, tales como el color del trazo, el color de fondo del recinto de firmado, el texto que queramos que aparezca a modo de informaci&oacute;n, as&iacute; como los eventos que se ejecutar&aacute;n al finalizar la firma o al querer borrar la misma</p>
<p> </p>
<div class="wp-block-image"><img src="https://lh4.googleusercontent.com/MsugpiGZqti4ViBnN_CLeH8ARGPN41doTmu3En7ad0ma1gtvy7oDtIFsSID-VWI-XnwrAE4bXF14yJAdn4kcc_Y8W-XhZUEKgwRMoX-y3XmxwPwmHEMbieyaxKX-X-Kpyc5iTUdg" alt="" /><br />
<div class="wp-block-image"><br />
<p>De esta forma, cualquiera trazo que se dibuje encima de la l&iacute;nea se podr&aacute; recoger como una firma que estar&aacute; disponible en formato imagen.</p>
<br>
<br>
<br>
<br>
<br>]]></content:encoded>
</item><item>
<title><![CDATA[Joomla protected: Mejorando la seguridad de tu Joomla]]></title>
<link>https://betabeers.com/blog/joomla-protected-mejorando-la-seguridad-tu-joomla-385/</link>
<pubDate>Thu, 06 Dec 2018 19:23:47 +0000</pubDate>
<category>Noticias</category>
<content:encoded><![CDATA[<p><span id="docs-internal-guid-7de1244f-7fff-829e-cbe8-75ed39500a78" style="font-size: 12pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: bold; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><img style="border: none; transform: rotate(0rad); display: block; margin-left: auto; margin-right: auto;" src="https://lh6.googleusercontent.com/SdSE30V7PXF8lhVLYTRFp66swGrpAKgu-ZYO-1WJREr89oTUO5QHwrV5IXB_JnQx67ldhcELCxbwcTtQIjbwAg5jTH3wo6ij8kdrgcPn9lYVk4V3739In5hjTRy98KjVa9L5fOxP" alt="" width="400" height="274" /></span></p>
<p> </p>
<p> </p>
<p id="docs-internal-guid-b0015a71-7fff-cdc5-2226-b8b1d5826954" style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"><span style="font-size: 12pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: bold; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Joomla para todo aquel que no lo pudiera conocer es un Sistema de Gesti&oacute;n de Contenidos CMD de sus siglas en ingl&eacute;s, que permite crear sitios WEB din&aacute;micos. Es un software de c&oacute;digo abierto creado en PHP liberado bajo licencia p&uacute;blica GNU. Necesita para su funcionamiento de una base de datos creada sobre MySQL y ser instalado en un servidor HTTP Apache, con lo cual aparte de las vulnerabilidades que pudieran tener esos sistemas que lo complementan, tambi&eacute;n hay que prestar especial atenci&oacute;n en la configuraci&oacute;n de seguridad en Joomla.</span></p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"> </p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"> </p>
<p id="docs-internal-guid-60a4ba0a-7fff-5636-a336-45d645a362b3" style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"><span style="font-size: 12pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">La seguridad de todos los servicios expuestos en internet siempre debe de ser revisada y comprobada a la vez que actualizada, ya que hay complementos que se pueden volver obsoletos, aunque eso s&iacute; los consejos generales que a la vez son comunes a todas las tecnolog&iacute;as siempre se deben de tener en cuenta, por ello vamos a hacer un breve repaso de ellos:</span></p>
<ol>
<li style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr">
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"><span style="font-size: 12pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: bold; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Realizar copias de seguridad.</span></p>
</li>
<li>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"><span style="font-size: 12pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: bold; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Elegir un hosting seguro y de confianza, lo m&aacute;s barato suele salir caro.</span></p>
</li>
<li><span id="docs-internal-guid-44a13d22-7fff-8b50-ea34-4252ad2624b8" style="font-size: 12pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: bold; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Mantener las actualizaciones al dia.</span></li>
<li><span id="docs-internal-guid-d360f798-7fff-e875-4b56-43e6c89b7a27" style="font-size: 12pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: bold; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Tener precauci&oacute;n con los Plugins, Extensiones y M&oacute;dulos que instalamos puesto que pueden ser maliciosos.</span></li>
<li><span id="docs-internal-guid-85e92020-7fff-2979-67ff-d15311db1942" style="font-size: 12pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: bold; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Utilizar el principio de m&iacute;nimo privilegio, dando los permisos de acceso escritura y lectura estrictamente necesarios.</span></li>
<li><span id="docs-internal-guid-687acc01-7fff-656b-12b8-bdd2445878b6" style="font-size: 12pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: bold; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Mantener el tanto el equipo de trabajo en local o en remoto debidamente actualizado y protegido.</span></li>
<li><span style="font-size: 12pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: bold; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Protegerse con contrase&ntilde;as fuertes y seguras ( recordad nunca admin, admin).</span></li>
</ol>
<p id="docs-internal-guid-cbc8b988-7fff-92a9-6313-d237da17c066" style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"><span style="font-size: 12pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">En Joomla una cosa en particular a tener en cuenta a la hora de establecer la seguridad es cambiar el prefijo de la tabla, puesto que no solo contiene la informaci&oacute;n de nuestros usuarios sino que tambi&eacute;n nuestra informaci&oacute;n, como todos los usuarios de Joomla saben, este es</span><span style="font-size: 12pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: bold; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> &ldquo;jos_&rdquo; </span><span style="font-size: 12pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">algo muy jugoso para un atacante a nuestra WEB. Esta operaci&oacute;n se debe llevar a cabo durante la instalaci&oacute;n aunque se podr&aacute; realizar despu&eacute;s pero es important&iacute;simo, hacerlo despu&eacute;s de una copia de seguridad, no sea que pudi&eacute;ramos perder nuestro proyecto completamente.</span></p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"><span style="font-size: 12pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Adem&aacute;s de todo lo anterior existen Plugin de seguridad muy buenos y eficientes como los que se van a mostrar en las siguientes l&iacute;neas: </span></p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"> </p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt;" dir="ltr"><span style="font-size: 12pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span id="docs-internal-guid-6f411b63-7fff-d4f6-29a3-6989f2fdd33c" style="font-size: 12pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><img style="border: none; transform: rotate(0rad); display: block; margin-left: auto; margin-right: auto;" src="https://lh4.googleusercontent.com/_a26jW_XEKZKEkIyNqwi9f1mCcYnzgPgoD3kgS1tigIp4e2Okq8dHDbFDh5Raf7xrBPZeyBaQo1qpaNCNEFOxIO92Mqi1z2NDpgMQ1cufl2QeJYwqaK3CVZNT3KZmmpFpuCPNDMC" alt="" width="294" height="68" /></span></span></p>
<p> </p>
<p id="docs-internal-guid-1c29b89b-7fff-23e0-29b9-5dddcdfda00c" style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"><span style="font-size: 12pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> Es un servicio de seguridad creado espec&iacute;ficamente para este CMD el cual protege al sitio Joomla de intrusiones:</span></p>
<p> </p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-indent: 36pt; text-align: justify;" dir="ltr"><span style="font-size: 12pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">1.Analiza tus archivos en busca de malware</span></p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-indent: 36pt; text-align: justify;" dir="ltr"><span style="font-size: 12pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> 2.Protege tu carpeta / administrador con una contrase&ntilde;a extra</span></p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-indent: 36pt; text-align: justify;" dir="ltr"><span style="font-size: 12pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> 3.Comprobar la integridad de los archivos principales.</span></p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-indent: 36pt; text-align: justify;" dir="ltr"><span style="font-size: 12pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> 4. Protege las cuentas de administrador</span></p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-indent: 36pt; text-align: justify;" dir="ltr"><span style="font-size: 12pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> 5.Encuentra y corrige permisos de archivos y carpetas inseguros.</span></p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-indent: 36pt; text-align: justify;" dir="ltr"><span style="font-size: 12pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> 6. Bloquear el acceso a pa&iacute;ses espec&iacute;ficos.</span></p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-indent: 36pt; text-align: justify;" dir="ltr"><span style="font-size: 12pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> 7.Bloquea autom&aacute;ticamente las direcciones IP que atacan tu sitio web.</span></p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-indent: 36pt; text-align: justify;" dir="ltr"> </p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-indent: 36pt; text-align: justify;" dir="ltr"> </p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-indent: 36pt;" dir="ltr"><span style="font-size: 12pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span id="docs-internal-guid-b018a80e-7fff-aaa4-c373-083595b05488" style="font-size: 12pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Eso s&iacute; esta extensi&oacute;n tiene un m&oacute;dico precio que para la seguridad que aporta a nuestros sitio pudiera ser muy bajo, 49 euros puesto que es muy completo e intuitivo aparte est&aacute; disponible para la mayor&iacute;a de los idiomas.</span></span></p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-indent: 36pt;" dir="ltr"> </p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-indent: 36pt;" dir="ltr"><span style="font-size: 12pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span style="font-size: 12pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span id="docs-internal-guid-359bca4d-7fff-345c-cc3c-cca650d5a9b7" style="font-size: 12pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><img style="border: none; transform: rotate(0rad); display: block; margin-left: auto; margin-right: auto;" src="https://lh5.googleusercontent.com/g2dyB2LFhHj5JSUlRHunK8iS61bnrBg0tAfg1hTFhnE1adcbKKrjE7OROLZwpvR_FLI7bkh4J1pSdeV3xBmAWeRaBl6h8Zzyb9jJutjE_nOOVgO6O9RjfeUPI_LsUVmbxYkCcoVK" alt="" width="638" height="264" /></span></span></span></p>
<p id="docs-internal-guid-48987b21-7fff-8434-340a-bdd1930baeed" style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"><span style="font-size: 12pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">ste plug-in monitorea continuamente tu Joomla, analiza si se han cambiado los detalles cr&iacute;ticos de la cuenta de usuario o si los directorios y archivos se han modificado. Por lo tanto, proporciona una l&iacute;nea de defensa adicional contra los intentos de pirater&iacute;a de sitios web. El complemento ralentiza los intentos de inicio de sesi&oacute;n de back-end repetidos para realizar ataques de fuerza bruta. Opcionalmente tambi&eacute;n incluye un servicio de monitoreo en remoto.</span></p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-indent: 36pt;" dir="ltr"> </p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"><span style="font-size: 12pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Como todo no es perfecto y no bloquea todos los intentos ni garantiza que nuestro sitio nunca sea vulnerado, pero s&iacute; que es un buen complemento ya que comunica a los administradores estos intentos fallidos de ataque para que no pasen desapercibidos. Este plug-in tiene una versi&oacute;n de prueba y otra standard m&aacute;s completa lo que depender&iacute;a de nuestras necesidades y de nuestro presupuesto.</span></p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"> </p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"> </p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt;" dir="ltr"><span style="font-size: 12pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span id="docs-internal-guid-b9f75607-7fff-c3a4-4a30-9af8ca0df704" style="font-size: 12pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><img style="border: none; transform: rotate(0rad); display: block; margin-left: auto; margin-right: auto;" src="https://lh5.googleusercontent.com/3agDABWtNY7EdkHbOq9RNqmE7vy6lML3tiWwz-urBAaimGCsgdRfrxpU5Z8iVoab7CctaMJ1NGAbJVmh6opxbaXuWHK9-E7q8xLzsw6gqnMc3p2K3vOA5SaVAMyvXO6gp3Wce-Tm" alt="" width="602" height="231" /></span></span></p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"> </p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"> </p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"> </p>
<p id="docs-internal-guid-47682635-7fff-2431-0951-a48703f14d96" style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"><span style="font-size: 12pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: bold; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">JhackGuard</span><span style="font-size: 12pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> est&aacute; dise&ntilde;ado para proteger nuestro sitio Joomla de ataques SQL de los que hablamos en mi anterior post ademas de ejecuci&oacute;n de c&oacute;digo remoto XSS, lo cual es muy interesante puesto que son los dos tipos de ataques m&aacute;s ejecutados. Una gran ventaja frente a otros productos es que tan solo necesitamos descargarlo, instalarlo y habilitarlo sin ninguna otra configuraci&oacute;n. Adem&aacute;s algo a tener muy encuenta es que es de software libre al menos para los apasionados de este tipo de software.</span></p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"> </p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"><span style="font-size: 12pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: bold; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Conclusi&oacute;n.</span></p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"><span style="font-size: 12pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Desde el punto de vista de la seguridad solo hay una cosa m&aacute;s importante implementarla cuanto antes, por supuesto mucho antes de poner en producci&oacute;n nuestro producto. Todas estas medidas i plug-in tan solo seria a mi parecer lo m&iacute;nimo que se deber&iacute;a implementar en nuestro sitio siempre pensando desde el punto de vista de la funcionalidad claro esta, por que si nuestro sitio es muy seguro pero no es funcional nadie lo utilizara.</span></p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"> </p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"> </p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"> </p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"> </p>
<p id="docs-internal-guid-d81016e3-7fff-9c1e-8031-37f13994f36a" style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"><span style="font-size: 12pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: bold; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Para m&aacute;s informaci&oacute;n.</span></p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"> </p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"><a style="text-decoration: none;" href="https://www.extensions.joomla.org/extension/"><span style="font-size: 12pt; font-family: Ubuntu; color: #1155cc; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: underline; -webkit-text-decoration-skip: none; text-decoration-skip-ink: none; vertical-align: baseline; white-space: pre-wrap;">https://www.extensions.joomla.org/extension/</span></a></p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"> </p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-indent: 36pt;" dir="ltr"><br /><br /></p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"> </p>]]></content:encoded>
</item><item>
<title><![CDATA[MotionLayout: CustomAttribute, KeyFrameSet y KeyPosition #AndroidMeetsKotlin]]></title>
<link>https://betabeers.com/blog/motionlayout-customattribute-keyframeset-keyposition-androidmeetskotlin-384/</link>
<pubDate>Fri, 30 Nov 2018 09:08:26 +0000</pubDate>
<category>Colaboraciones</category>
<content:encoded><![CDATA[<h2 id="h.qah8iwynvvey" class="c3"><img style="display: block; margin-left: auto; margin-right: auto;" src="http://betabeers.com/uploads/blog/20181130_image_1.png" alt="" width="600" height="293" /></h2>
<p class="c4">Una vez has visto qu&eacute; es MotionLayout y qu&eacute; puedes hacer con &eacute;l, seguir&aacute;s avanzando en la personalizaci&oacute;n de las animaciones. Para ello, vas a ver la &uacute;ltima interacci&oacute;n de usuario que puede comenzar una animaci&oacute;n de este tipo y terminar&aacute;s conociendo tres elementos que otorgan un toque de UX (Experiencia de Usuario) que puede destacar tu app sobre el resto. Sin m&aacute;s, &iexcl;a por ello!</p>
<p class="c4 c5"> </p>
<h2 id="h.5wrfdeif32oj" class="c3">Elemento OnClick</h2>
<p class="c4">En el <a class="c8" href="https://www.google.com/url?q=https://betabeers.com/blog/motionlayout-motionscene-la-simpleza-las-animaciones-cada-vez-mas-cerca-androidmeetskotlin-380/&amp;sa=D&amp;ust=1543565194895000">post anterior</a> ya viste el elemento OnSwipe (dentro de Transition), en este caso, vas a ver la interacci&oacute;n m&aacute;s com&uacute;n e intuitiva de una vista, el click. Este elemento s&oacute;lo tiene dos atributos posibles:</p>
<ul class="c16 lst-kix_kulvwcnljs4z-0 start">
<li class="c4 c6"><strong>target</strong>. Indica la vista que se tomar&aacute; para capturar el evento de pulsado.</li>
<li class="c4 c6"><strong>mode</strong>. Indica qu&eacute; har&aacute; la animaci&oacute;n una vez se ha producido el evento. Tiene los siguientes valores: &ldquo;transitionToEnd&rdquo;, &ldquo;toggle&rdquo;, &ldquo;transitionToStart&rdquo;, &ldquo;jumpToEnd&rdquo;, &ldquo;jumpToStart&rdquo;. En el caso de &ldquo;toggle&rdquo; lo que har&aacute; ser&aacute; pasar a la animaci&oacute;n de fin si se est&aacute; en el estado de inicio y viceversa. Para los dos &uacute;ltimos, se pasar&aacute; directamente a las constraints que indiquen cada uno pero sin realizar animaci&oacute;n alguna.</li>
</ul>
<p class="c4 c5"> </p>
<p class="c4">La animaci&oacute;n que har&aacute;s durante el post ser&aacute; lanzada por un evento de pulsado en vez del desplazamiento. En cuanto al c&oacute;digo, seguimos sin ver ni una l&iacute;nea en Java o Kotlin, s&oacute;lo har&aacute;s uso de c&oacute;digo XML.</p>
<p class="c4 c5"> </p>
<p class="c4"><img src="http://betabeers.com/uploads/blog/20181130_image_2.png" alt="" width="450" height="216" /></p>
<p class="c4 c5"> </p>
<h2 id="h.es34m2xjm1ke" class="c3">KeyFrameSet</h2>
<p class="c4">Otro de los elementos que nos aporta Transition es &ldquo;KeyFrameSet&rdquo;, &eacute;ste es capaz de a&ntilde;adir puntos intermedios que modifican el path (ruta) de la animaci&oacute;n que se establece entre dos ConstraintSets. &Eacute;ste tiene adem&aacute;s los siguientes componentes:</p>
<h4 id="h.6wgzw68kq7ym" class="c7">KeyPosition.</h4>
<p class="c4">Controla la posici&oacute;n del layout durante la animaci&oacute;n. Sus atributos son:</p>
<ul class="c16 lst-kix_ufyyal7oa8x-0 start">
<li class="c4 c6"><strong>target</strong>. Id de la vista.</li>
<li class="c4 c6"><strong>framePosition</strong>. Define el punto a lo largo de la interpolaci&oacute;n 0 = inicio, 100 = final.</li>
<li class="c4 c6"><strong>transitionEasing</strong>. Establece una curva de suavizado que se usar&aacute; al llegar a este punto (por ejemplo, curve(1.0, 0, 0 ,1.0))</li>
<li class="c4 c6"><strong>type</strong>. Indica c&oacute;mo se calcular&aacute; la desviaci&oacute;n para el trayecto lineal. Dos valores: &ldquo;deltaRelative&rdquo; y &ldquo;pathRelative&rdquo;.</li>
<li class="c4 c6"><strong>percentX</strong>. (float) Distancia porcentual del principio al fin a lo largo del eje X (deltaRelative) o a lo largo de la ruta (path) de la animaci&oacute;n (pathRelative).</li>
<li class="c4 c6"><strong>percentY</strong>. (float) Lo mismo que el anterior pero con el eje Y.</li>
<li class="c4 c6"><strong>curveFit</strong>. La ruta de la animaci&oacute;n ser&aacute; trazada.</li>
<li class="c4 c6"><strong>drawPath</strong>. Dibuja la ruta de los objetos.</li>
<li class="c4 c6"><strong>sizePercent</strong>. Si la vista cambia de tama&ntilde;o, este atributo controla c&oacute;mo crece el tama&ntilde;o. (para objetos de tama&ntilde;o fijo usa KeyAttributes scaleX / Y)</li>
<li class="c4 c6"><strong>curveFit</strong>. Selecciona una ruta basada en l&iacute;neas rectas o una curva. Dos valores: &ldquo;linear&rdquo; &oacute; &ldquo;spline&rdquo;.</li>
</ul>
<p class="c4 c5"> </p>
<h4 id="h.wwln30joj3u0" class="c7">KeyAttribute.</h4>
<p class="c4">Puede cambiar los valores de un atributo del layout mientras es animado. Adem&aacute;s de tener los atributos ya definidos (&ldquo;target&rdquo;, &ldquo;framePosition&rdquo;, &ldquo;curveFit&rdquo;, &ldquo;transitionEasing&rdquo; y &ldquo;drawPath&rdquo;), nos encontramos con los siguientes:</p>
<ul class="c16 lst-kix_ocut5rs6o03f-0 start">
<li class="c4 c6"><strong>transitionPathRotate</strong>. (float) Indica la rotaci&oacute;n del objeto durante la animaci&oacute;n.</li>
<li class="c4 c6"><strong>progress</strong>. Llama al m&eacute;todo &ldquo;setProgress(float)&rdquo; para esta vista.</li>
<li class="c4 c6"><strong>&lt;CustomAttribute&gt;</strong>. Llamar&aacute; al m&eacute;todo &ldquo;setNombreDelAtributo&rdquo; mediante reflexi&oacute;n.</li>
</ul>
<p class="c4 c5"> </p>
<p class="c4">Tiene adem&aacute;s todos los atributos definidos en: <a class="c8" href="https://www.google.com/url?q=https://developer.android.com/reference/android/support/constraint/motion/MotionLayout&amp;sa=D&amp;ust=1543565194901000">https://developer.android.com/reference/android/support/constraint/motion/MotionLayout</a></p>
<p class="c4 c5"> </p>
<h4 id="h.dh6lq4cemepc" class="c7">CustomAttribute</h4>
<p class="c4">Es similar a usar el m&eacute;todo &ldquo;setMyAttr(tipo)&rdquo;. Para este elemento tenemos:</p>
<ul class="c16 lst-kix_e3fvwgh3d4y9-0 start">
<li class="c4 c6"><strong>attributeName</strong>. Nombre del atributo, es importante saber que es sensible a may&uacute;sculas y min&uacute;sculas.</li>
<li class="c4 c6"><strong>customColorValue</strong>.</li>
<li class="c4 c6"><strong>customIntegerValue</strong>.</li>
<li class="c4 c6"><strong>customFloatValue</strong>.</li>
<li class="c4 c6"><strong>customStringValue</strong>.</li>
<li class="c4 c6"><strong>customDimension</strong>.</li>
<li class="c4 c6"><strong>customBoolean</strong>.</li>
</ul>
<p class="c4 c5"> </p>
<p class="c4">Una vez tienes definidos todos los componentes y atributos, vas a realizar una animaci&oacute;n haciendo uso de ellos. &iexcl;a por ella!</p>
<h2 id="h.5mczh58et7oz" class="c3">Definici&oacute;n del nuevo MotionScene</h2>
<p class="c4">Usar&aacute;s las vistas definidas en el post anterior y s&oacute;lo cambiar&aacute;s la animaci&oacute;n mediante su nuevo MotionScene. La animaci&oacute;n que vas a realizar la puedes ver en el <a class="c8" href="https://www.google.com/url?q=https://github.com/franlopjur/MotionLayout-parte-2-/blob/master/animacion_v2.gif&amp;sa=D&amp;ust=1543565194904000">siguiente gif</a>.</p>
<p class="c4 c5"> </p>
<h3 id="h.llm4i2wyeeo4" class="c15">Define el comienzo de la animaci&oacute;n mediante un OnClick</h3>
<p class="c4">Simplemente tienes que usar el elemento &ldquo;OnClick&rdquo; y especificarle el target al que se le aplicar&aacute; y el modo. En este caso ser&aacute; de tipo &ldquo;toggle&rdquo;.</p>
<p class="c4"><img src="http://betabeers.com/uploads/blog/20181130_image_3.png" alt="" width="350" height="231" /></p>
<h3 id="h.251df32io774" class="c15">Cambia el path de las vistas con KeyFrameSet y KeyPosition</h3>
<p class="c4">Para la vista C, tienes que definir dos KeyPosition, los cuales har&aacute;n:</p>
<ul class="c16 lst-kix_2b06gl646b0a-0 start">
<li class="c4 c6">En el momento 20 (recuerda que es como el 20% de la animaci&oacute;n ya que va de 0 a 100) la vista vaya al 65% del eje X de la pantalla. Nos referimos al porcentaje del eje X al definir el tipo como &ldquo;parentRelative&rdquo;.</li>
<li class="c4 c6">En el momento 80 (80% de la animaci&oacute;n), la vista llegar&aacute; hasta el 90% del eje X y lo har&aacute; de forma relativa tal y como se ha definido.</li>
</ul>
<p class="c4 c5 c18"> </p>
<p class="c4">El c&oacute;digo ser&iacute;a el siguiente:</p>
<p class="c4"><img src="http://betabeers.com/uploads/blog/20181130_image_4.png" alt="" width="350" height="231" /></p>
<h3 id="h.mba1s2sn06lv" class="c15">Cambia varios atributos mientras se realiza la animaci&oacute;n</h3>
<p class="c4">Para la vista C tambi&eacute;n debes establecer que su atributo &ldquo;backgroundColor&rdquo; cambie desde la animaci&oacute;n de inicio a la de fin. Para ello usa el elemento &ldquo;CustomAttribute&rdquo; con sus propiedades &ldquo;attributeName&rdquo;, con valor &ldquo;backgroundColor&rdquo; y &ldquo;customColorValue&rdquo; pas&aacute;ndole el color que quieras desde el inicio (transparente en este caso) al fin (az&uacute;l).</p>
<p class="c4 c5"> </p>
<p class="c4">El siguiente c&oacute;digo estar&aacute; en el ConstraintSet de inicio:</p>
<p class="c4"><img src="http://betabeers.com/uploads/blog/20181130_image_5.png" alt="" width="400" height="172" /></p>
<p class="c4 c5"> </p>
<p class="c4">&Eacute;ste estar&aacute; en el ConstraintSet de fin:</p>
<p class="c4"><img src="http://betabeers.com/uploads/blog/20181130_image_6.png" alt="" width="400" height="198" /></p>
<p class="c4 c5"> </p>
<p class="c4 c5"> </p>
<p class="c4">El c&oacute;digo del MotionScene puedes verlo en el siguiente <a class="c8" href="https://www.google.com/url?q=https://github.com/franlopjur/MotionLayout-parte-2-/blob/master/animation_with_custom_attributes.xml&amp;sa=D&amp;ust=1543565194908000">enlace</a>.</p>
<p class="c4 c5"> </p>
<p class="c4">Finalmente, sigues viendo que las animaciones con MotionLayout son realmente sencillas y no necesitamos conocer grandes cosas para realizar animaciones con una complejidad media. Te animo a seguir indagando y espero que te haya gustado.</p>
<p class="c4 c5"> </p>
<p class="c4 c5"> </p>]]></content:encoded>
</item><item>
<title><![CDATA[Cómo preparar nuestras instalaciones de WordPress para Gutenberg]]></title>
<link>https://betabeers.com/blog/como-preparar-nuestras-instalaciones-wordpress-gutenberg-383/</link>
<pubDate>Fri, 23 Nov 2018 08:36:26 +0000</pubDate>
<category>Colaboraciones</category>
<content:encoded><![CDATA[<p><a href="https://wordpress.org/gutenberg/">Gutenberg</a> es el <strong>nuevo editor de bloques para WordPress</strong>. En la futura e inminente <a href="https://make.wordpress.org/core/2018/11/16/5-0-gutenberg-status-update-nov-16/">versi&oacute;n de WordPress 5.0</a> aparecer&aacute; como editor de contenido por defecto para nuestras entradas, p&aacute;ginas y cualquier otro tipo de contenido personalizado que tengamos instalado, lo que har&aacute; que, todo aquel que no haya hecho los deberes previos, pueda llevarse una sorpresa.</p>
<p>El objetivo principal del proyecto Gutenberg es aportar a la comunidad de usuarios de WordPress <strong>una forma de publicar contenido m&aacute;s sencilla, intuitiva y con m&aacute;s posibilidades de maquetaci&oacute;n</strong> de las que ten&iacute;amos hasta el momento.</p>
<p><img style="display: block; margin-left: auto; margin-right: auto;" title="WordPress Gutenberg" src="http://betabeers.com/uploads/blog/20181123_articulo-betabeers-gutenberg.jpg" alt="WordPress Gutenberg" width="600" height="400" /></p>
<p>El editor por defecto en WordPress era <strong>el afamado TinyMCE</strong>, un editor HTML del tipo WYSIWYG, que deja paso a una nueva era con el fin de adaptarse a las necesidades de gran parte de los editores de contenido, y tambi&eacute;n, por qu&eacute; no decirlo, ponerse a la altura de otros sistemas de publicaci&oacute;n web existentes en el mercado que ofrecen mejores posibilidades en este &aacute;rea.</p>
<p>Durante el &uacute;ltimo a&ntilde;o se ha estado trabajando en varios frentes para que el proceso de adaptaci&oacute;n al <strong>editor de bloques de Gutenberg</strong> sea lo m&aacute;s fluido posible.</p>
<p>Gutenberg nos ofrece bastantes ventajas a tener en cuenta.</p>
<h2><span>Ventajas de Gutenberg para desarrolladores</span></h2>
<h3><span>Fin de la dependencia con TinyMCE</span></h3>
<p>Gutenberg rompe con la <strong>dependencia de WordPress con TinyMCE</strong> y abre una nueva era para los desarrolladores que podr&aacute;n implementar sus propios bloques personalizados para el editor.</p>
<p>Esta situaci&oacute;n abre posibilidades de mejora en la edici&oacute;n y creatividad en un entorno donde todo parec&iacute;a estar ya inventado.</p>
<h2><span>Incorporaci&oacute;n de JavaScript en fases de desarrollo para WordPress</span></h2>
<p>Tras diferentes divergencias en el proceso inicial de desarrollo de Gutenberg, ha sido React quien ha ganado la partida, si bien, la creaci&oacute;n de bloques personalizados podr&aacute; generarse mediante <strong>el uso de diferentes frameworks JavaScript</strong> como Vue.js o el propio React.</p>
<p>Este cambio en la composici&oacute;n, puede relegar a PHP a medio plazo como pieza clave en la creaci&oacute;n de plugins, extensiones y temas para WordPress. Con el lanzamiento de WP REST API ya pudimos ver avances en este sentido, pero cada vez se nos hace m&aacute;s presente que JavaScript tendr&aacute; mucho que ver en el mundo del desarrollo para WordPress.</p>
<h3><span>Adaptaci&oacute;n de plugins y temas premium</span></h3>
<p>Todos los<strong> desarrolladores de temas y plugins</strong> ha tenido que estar al tanto de todo lo que suced&iacute;a en torno al lanzamiento de WordPress 5.0 y la incorporaci&oacute;n del nuevo editor de bloques con el fin de adaptar sus productos a esta nueva situaci&oacute;n.</p>
<p>Es tiempo tambi&eacute;n para aprovechar la ola y readaptar nuestros temas y plugins, o crear sustitutos de otros, con el fin de poder emparejarse a la perfecci&oacute;n con Gutenberg. Todo aquel que no haya estado lo suficientemente preparado se quedar&aacute; fuera, y ese espacio ser&aacute; aprovechado por desarrolladores que consigan suplir carencias del editor o aportarle nuevas caracter&iacute;sticas, tanto si se trata de funcionalidades de un plugin como de a&ntilde;adidos para un tema.</p>
<h2><span>Recomendaciones para prepararnos ante la llegada de Gutenberg</span></h2>
<p>Desde hace meses disponemos del <strong>plugin Gutenberg en el repositorio de WordPress</strong> para poder probarlo en nuestras instalaciones. Esto ha permitido que de manera independiente al core de WordPress, los miembros de la comunidad de WordPress hayan podido participar en el testing de la herramienta antes de que vaya a ser incorporada en el n&uacute;cleo.</p>
<p>De todos modos, si estamos a&uacute;n en fase de pruebas en nuestros desarrollos o sitios web en producci&oacute;n, es conveniente tener en cuenta algunas medidas que deber&iacute;amos empezar a tomar ya mismo para que podamos evitar cualquier tipo de error que se pueda producir en nuestro proceso de publicaci&oacute;n.</p>
<h3><span>Prueba el plugin Gutenberg</span></h3>
<p>En estos momentos nos encontramos en la versi&oacute;n 4.2 del <a href="https://wordpress.org/plugins/gutenberg/">plugin Gutenberg</a> incorpora la pr&aacute;ctica totalidad de elementos que tendr&aacute; la versi&oacute;n final. Podemos adelantarnos a la incorporaci&oacute;n en el n&uacute;cleo de WordPress.</p>
<p>Incluso podr&iacute;amos plantearnos la posibilidad de probar directamente la versi&oacute;n <a href="https://make.wordpress.org/core/5-0/">RC de WordPress 5.0</a> con el fin de testear todas las nuevas funcionalidades en un entorno de pruebas.</p>
<h3><span>Prueba la compatibilidad con tus plugins y temas</span></h3>
<p>Una vez que hayamos probado el propio comportamiento de Gutenberg es momento de ponerlo en acci&oacute;n con otros plugins fundamentales que est&eacute;n en funcionamiento en nuestros sitios web para poder buscar <strong>soluciones o alternativas de manera anticipada</strong>.</p>
<h3><span>Instala el plugin Classic Editor</span></h3>
<p>Como la comunidad de WordPress es tan grande y participativa, siempre aparecen soluciones intermedias para casos concretos, como puede ser <strong>la posibilidad de tener que volver atr&aacute;s a nuestro antiguo editor</strong> por incompatibilidades no detectadas a tiempo.</p>
<p>Para este caso, disponemos de <a href="https://es.wordpress.org/plugins/classic-editor/">Classic Editor</a>, un plugin que tras su activaci&oacute;n elimina cualquier rastro del nuevo editor de bloques, y permite recuperar la versi&oacute;n anterior de la edici&oacute;n de p&aacute;ginas y entradas en WordPress.</p>
<h3><span>Crea un proceso de testing personalizado para tus proyectos</span></h3>
<p>Cuando trabajas con multitud de proyectos web, y ofrece mantenimiento y soporte a muchos de ellos, es importante que establezcas una hoja de ruta a seguir para poder realizar una revisi&oacute;n de cada uno de los sitios web sin dejar cabos sueltos.</p>
<p>Este es <strong>el procedimiento que estoy siguiendo actualmente y que te recomiendo</strong>, aunque puedes completarlo t&uacute; mismo con tus propios aportes y experiencia.</p>
<ul>
<li>Instala el plugin Classic Press de manera anticipada para poder conservar un escenario con las opciones del nuevo editor de bloques de Gutenberg, pero con las posibilidades de recuperar las opciones anteriores.</li>
<li>Crea un entorno de pruebas o staging donde poder actualizar a la versi&oacute;n de WordPress 5.0 en fase beta con la incorporaci&oacute;n de Gutenberg.</li>
<li>Activa y actualiza cada uno de los plugins en funcionamiento en el entorno de pruebas comprobando su correcto funcionamiento y compatibilidad.</li>
<li>Crea una lista de plugins y temas en funcionamiento en tus proyectos y revisa su estado de compatibilidad con el editor de bloques seg&uacute;n las pruebas anteriores. De este modo podr&aacute;s ahorrar el proceso en sitios web que utilicen las mismas herramientas ya probadas.</li>
</ul>
<p>Si quieres estar al tanto de cualquier novedad relacionada con la llegada de Gutenberg y su implementaci&oacute;n en el core de WordPress te recomiendo algunas fuentes adicionales como <a href="https://gutenberg.news/">Gutenberg News</a> o la newsletter semanal de <a href="https://www.aprendegutenberg.com/">Aprende Gutenberg</a>.</p>
<h3><span>Actualizaci&oacute;n</span></h3>
<p>La &uacute;ltima fecha confirmada acerca de la publicaci&oacute;n de la versi&oacute;n 5.0 de WordPress incorporando es el martes 27 de noviembre seg&uacute;n <a href="https://make.wordpress.org/core/2018/11/09/update-on-5-0-release-schedule/">el propio blog de Make WordPress</a>.</p>]]></content:encoded>
</item><item>
<title><![CDATA[Notificaciones Push - Funcionamiento, alternativas y posibilidades]]></title>
<link>https://betabeers.com/blog/notificaciones-push-funcionamiento-alternativas-posibilidades-382/</link>
<pubDate>Fri, 16 Nov 2018 11:45:57 +0000</pubDate>
<category>Colaboraciones</category>
<content:encoded><![CDATA[<p><em>Este art&iacute;culo ha sido escrito por <a href="https://twitter.com/sirillo47">Siro Ram&iacute;rez</a> y publicado originalmente en el blog de <a href="https://solidgeargroup.com/notificaciones-push-funcionamiento-alternativas-y-posibilidades?lang=es" target="_blank">Solid GEAR</a>.</em></p>
<h2><strong>&iquest;Conoces todas las alternativas de notificaciones PUSH existentes hoy en d&iacute;a y c&oacute;mo funcionan?</strong></h2>
<p>Porque supongo que a estas alturas sabr&aacute;s lo que es una notificaci&oacute;n PUSH, &iquest;verdad? Esos mensajes que recibimos aunque la aplicaci&oacute;n est&eacute; cerrada, y que nos permiten informar al usuario de eventos importantes (o al menos deber&iacute;a usarse para eso) de una manera mucho menos costosa para el m&oacute;vil que tener la app levantada y estar preguntando constantemente si ha ocurrido algo (a este otro sistema se le conoce como PULL).</p>
<p style="text-align: center;"><img class="wp-image-6688 aligncenter" src="https://solidgeargroup.com/wp-content/uploads/2018/11/push_resized.png" alt="push whatsapp" width="336" height="336" /></p>
<p>Pero lo que a lo mejor a&uacute;n no sabes si no te ha tocado trabajar con ello es c&oacute;mo se implementa esta parte, o lo que es peor, puedes perderte en largas p&aacute;ginas de documentaci&oacute;n, diferentes alternativas, liarte y no saber por donde empezar, cuando el esquema general es bastante sencillo y la mayor&iacute;a de alternativas actuales siguen el mismo patr&oacute;n. Lo que pretende este post es darte una idea general (con links que contendr&aacute;n la informaci&oacute;n detallada para cada una de las plataformas) de qu&eacute; patr&oacute;n utilizar para implementar notificaciones PUSH dependiendo de tus necesidades.</p>
<h2>Notificaciones PUSH iOS - APNs</h2>
<p style="text-align: left;">Empezaremos hablando por el sistema posiblemente m&aacute;s cerrado y por ello uno de los m&aacute;s sencillos de entender de notificaciones PUSH. APNs es la plataforma principal mantenida por Apple para la gesti&oacute;n de env&iacute;o de notificaciones a sus dispositivos iOS. Como todos los sistemas de notificaciones van a requerir tanto configuraci&oacute;n en la parte del cliente m&oacute;vil como configuraci&oacute;n en la parte servidora de nuestro sistema, como adelantan en el que esquema de su plataforma:</p>
<p style="text-align: center;"><img class=" wp-image-6682 aligncenter" src="https://solidgeargroup.com/wp-content/uploads/2018/11/apnsPic.png" alt="apns" width="822" height="243" /></p>
<p>El primer paso es crear un provisioning profile para tu app iOS, la parte m&aacute;s complicada del proceso de notificaciones Apple. Podr&aacute;s ver informaci&oacute;n m&aacute;s detallada de como se hace <a href="https://help.apple.com/xcode/mac/current/#/devdfd3d04a1" rel="noopener" target="_blank">aqu&iacute;</a>. Pero no quiero que nos liemos con demasiado detalle, nos vamos a quedar con el concepto de que ese provisioning profile crea una asociaci&oacute;n entre tu dispositivo (identificado por el UDID) y tu aplicaci&oacute;n (con un App ID que la identifica de manera &uacute;nica para Apple)</p>
<p>Una vez tengas ese provisioning profile, vas a poder registrar tu dispositivo + aplicaci&oacute;n en la plataforma APNs. (Info detallada <a href="https://developer.apple.com/documentation/usernotifications/registering_your_app_with_apns" rel="noopener" target="_blank">aqu&iacute;</a>). Lo cual te va a devolver un token de notificaci&oacute;n que identifica tu m&oacute;vil y aplicaci&oacute;n en concreto. Una vez tengamos ese token, habr&aacute; que guardarlo en nuestro server de alguna manera, probablemente a trav&eacute;s de un API Rest. Con ese token (o toda la colecci&oacute;n de tokens que haya recibido el server), ya seremos capaces de, en el momento que queramos, contactar con la plataforma APNs para que env&iacute;e una notificaci&oacute;n.</p>
<p>Dependiendo la tecnolog&iacute;a / framework de servidor que utilicemos, probablemente haya librer&iacute;as creadas que nos faciliten mucho la labor, pero el concepto por debajo es tan sencillo como hacer una petici&oacute;n POST al servidor de APNs (api.push.apple.com:443) que contiene tu id de aplicaci&oacute;n, la lista de tokens y un payload con el contenido de la notificaci&oacute;n y unos cuantos par&aacute;metros adicionales que puedes ver <a href="https://developer.apple.com/library/archive/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/CommunicatingwithAPNs.html#//apple_ref/doc/uid/TP40008194-CH11-SW1" rel="noopener" target="_blank">aqu&iacute;</a>.</p>
<p>Seguimos?</p>
<h2><strong>Notificaciones PUSH Android - FCM</strong></h2>
<p>El segundo sistema del que vamos a tratar es Firebase Cloud Messaging, el sistema por defecto para Android. Atr&aacute;s quedaron ya los antiguos sistemas GCM o el anterior C2DM. Igual que con APNs, el primer paso es registrar registrar nuestro proyecto en la plataforma, en este caso Firebase. (Info detallada <a href="https://firebase.google.com/docs/cloud-messaging/android/client?hl=es-419" rel="noopener" target="_blank">aqu&iacute;</a>)</p>
<p>Una vez registrado, instalamos el sdk en la app, que al iniciarse generar&aacute; un token de registro para mandar notificaciones a ese dispositivo y aplicaci&oacute;n (os suena el proceso? exactamente igual que en APNs!)</p>
<p>As&iacute; que ya sab&eacute;is la parte que queda, guardar esos tokens en servidor y mandar un POST a la url de FCM (https://fcm.googleapis.com/fcm/send) con los tokens a incluir y el contenido de tu notificaci&oacute;n. Y si, seguro que hay ya librer&iacute;as que te facilitan mucho la vida al respecto.</p>
<h3>FCM - &iexcl;Y tenemos la Firebase console!</h3>
<p>Que no necesit&aacute;is server en vuestro sistema, quer&eacute;is hacer una prueba de concepto, etc. Nos podemos saltar la &uacute;ltima parte, pasarle los tokens a la consola de firebase y empezar a enviar notificaciones desde ah&iacute;. Una manera muy r&aacute;pida y sencilla de empezar a probar nuestras notificaciones.</p>
<h3 style="text-align: center;"><img class="wp-image-6695 aligncenter" src="https://solidgeargroup.com/wp-content/uploads/2018/11/firebase-console.png" alt="" width="483" height="309" /></h3>
<h3>FCM - &iexcl;Y es multiplataforma!</h3>
<p>Si, si, que quer&eacute;is enviar notificaciones a ambas plataformas, firebase se puede encargar por nosotros de hablar con APNs. Porque las notificaciones iOS tienen que pasar por APNs si o si. Pero centralizamos nuestra comunicaci&oacute;n con firebase y &eacute;l gestiona la comunicaci&oacute;n necesaria con APNs.</p>
<p>Para ello tendremos que:</p>
<ul>
<li>Agregar el proyecto iOS a Firebase (Info detallada <a href="https://firebase.google.com/docs/cloud-messaging/ios/client?hl=es-419" rel="noopener" target="_blank">aqu&iacute;</a>)</li>
<li>Agregar el sdk de Firebase a la app (en vez del de APNs)</li>
<li>Generar el perfil de aprovisionamiento igualmente en tu centro de programadores de Apple. (Info detallada <a href="https://firebase.google.com/docs/cloud-messaging/ios/certs?hl=es-419" rel="noopener" target="_blank">aqu&iacute;</a>)</li>
</ul>
<p>Y con eso, podemos enviar las notificaciones a ambas plataformas de la misma manera que lo har&iacute;amos para dispositivos Android.</p>
<h2>Notificaciones PUSH en Web</h2>
<p>Porque nuestro paradigma favorito de notificaciones para usuarios ya no es exclusivo de aplicaciones m&oacute;viles, &iexcl;sino que se puede usar para aplicaciones web! Que no tienen nada que ver. &iquest;O s&iacute;? (no adelantemos acontecimientos, ya que de eso hablaremos en el &uacute;ltimo punto)</p>
<p>Algunos navegadores ya soportan la especificaci&oacute;n del <a href="https://developer.mozilla.org/es/docs/Web/API/Push_API" rel="noopener" target="_blank">API Push W3C</a>, a&uacute;n en estado borrador y que viene muy de la mano del <a href="https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API" rel="noopener" target="_blank">API de service workers</a>. Posiblemente exista ya una librer&iacute;a que te facilite las cosas en el framework de desarrollo web que utilices, pero voy a intentar esquematizar el flujo que hay detr&aacute;s. Flujo que posiblemente te suena porque es muy similar al que ya hemos tratado en las aplicaciones m&oacute;viles.</p>
<p>Tu aplicaci&oacute;n web deber&aacute; tener un service worker asociado (de ah&iacute; la conexi&oacute;n obvia entre las push web y los service worker). Este service worker ser&aacute; el que tendr&aacute;s que utilizar para suscribirte al al servicio de notificaciones (Into detallada <a href="https://developer.mozilla.org/es/docs/Web/API/Push_API" rel="noopener" target="_blank">aqu&iacute;</a>) y obtener tu subscriptionId (si, lo mismito que el token de APNs y de FCM) y un endpoint al que llamar para enviar la notificaci&oacute;n (menos mal! porque aqu&iacute; todav&iacute;a no sab&iacute;amos si hab&iacute;a que llamar a la url de apns, fcm o a qui&eacute;n)</p>
<p>Y haciendo un POST a ese endpoint, con esos tokens y la info de la notificaci&oacute;n... voil&aacute;! tenemos notificaci&oacute;n en nuestra aplicaci&oacute;n web</p>
<h2>Notificaciones PUSH en apps h&iacute;bridas</h2>
<p>&iquest;A estas alturas no sabes lo que es una app h&iacute;brida? Pues te dejo unos links para que te pongas al d&iacute;a r&aacute;pido:</p>
<p style="text-align: center;"><a href="https://solidgeargroup.com/desarrollo-de-apps-hibridas-con-ionic?lang=es">Apps h&iacute;bridas con Ionic</a></p>
<p style="text-align: center;"><a href="https://solidgeargroup.com/desarrollo-multiplataforma-con-xamarin?lang=es">Desarrollo multiplataforma con Xamarin</a></p>
<p>Si ya sabes de lo que te hablamos, posiblemente sepas que el mundo de aplicaciones h&iacute;bridas es un terreno con m&uacute;ltiples alternativas. Existen tecnolog&iacute;as que nos proporcionan aplicaciones nativo-h&iacute;bridas como Xamarin o React Nativa. Apps h&iacute;bridas basadas en tecnolog&iacute;a web como Ionic. PWAs que posiblemente puedas crear ya con tu tecnolog&iacute;a web favorita..</p>
<p>Una vez que ya conocemos las alternativas anteriores, integrar notificaciones PUSH en nuestra app h&iacute;brida va a ser muy sencillo. Si se trata de una app nativa h&iacute;brida, buscaremos librer&iacute;as que nos permitan utilizar APNs o FCM y seguiremos los pasos descritos anteriormente. &iquest;Que est&aacute; basada en web? Repasemos la compatibilidad de navegadores e integremos una web push! (Importante repasar esta compatibilidad, ya que a fecha de creaci&oacute;n de este post no hay compatibilidad con ninguna versi&oacute;n de Safari y por lo tanto no se pueden integrar web PUSH para apps iOS)</p>
<p style="text-align: left;">Espero que con todo esto te queden mucho m&aacute;s claras las alternativas y esquema general a la hora de integrar notificaciones PUSH en tu app!</p>]]></content:encoded>
</item><item>
<title><![CDATA[Injection Is Broken. Que no te inyecten tu base de datos.]]></title>
<link>https://betabeers.com/blog/injection-is-broken-que-no-te-inyecten-tu-base-datos-381/</link>
<pubDate>Thu, 08 Nov 2018 19:13:04 +0000</pubDate>
<category>Colaboraciones</category>
<content:encoded><![CDATA[<p><img style="display: block; margin-left: auto; margin-right: auto;" src="http://betabeers.com/uploads/blog/20181108_InyectionIsBrocken.png" alt="" width="477" height="400" /></p>
<p dir="ltr"><strong><span>En todas las aplicaciones sean del tipo que sea tenemos bases de datos en las que poder guardar informaci&oacute;n necesaria para su funcionamiento o modelo de negocio, esa informaci&oacute;n no solo puede ser muy valiosa para la aplicaci&oacute;n o la empresa, adem&aacute;s puede contener datos de car&aacute;cter confidencial los cuales en caso de una fuga de informaci&oacute;n podr&iacute;a acarrear a la empresa responsable una multa de gran cuant&iacute;a se&ntilde;aladas dentro del RGPD (Reglamento General de protecci&oacute;n de Datos). En esta entrada vamos a ver de qu&eacute; trata un ataque que podr&iacute;an realizar contra nuestra base de datos, para ex filtrar informaci&oacute;n llamado SQL Injections y como intentar mitigar los errores de c&oacute;digo para intentar protegernos de este tipo de ataques.</span></strong></p>
<p> </p>
<p dir="ltr"><strong>Comillas con los codos &iquest;Que es SQL Injections?</strong></p>
<p> </p>
<p dir="ltr">Toda aplicaci&oacute;n que interact&uacute;a con datos de usuarios o de la empresa, adem&aacute;s de un sitio donde guardarlos (ll&aacute;mese Base de datos) va tener tambi&eacute;n un m&eacute;todo de introducci&oacute;n de dichos datos, ah&iacute; es donde se puede producir lo que en seguridad inform&aacute;tica llamamos &ldquo;introducir comillas con los codos&rdquo;, refiri&eacute;ndonos a introducir c&oacute;digo propio de consultas SQL los cuales contienen comillas, y se dice con los codos porque un atacante ni tan siquiera tiene por qu&eacute; saber que consulta debe realizar para extraer la informaci&oacute;n.</p>
<p> </p>
<p dir="ltr">En definitiva una inyecci&oacute;n SQL es una debilidad en los sistemas de bases de datos relacionales, accediendo a sus datos mediante Lenguaje SQL. El uso de los caracteres pertenecientes al lenguaje pueden permitir a usuarios no autorizados manipular, borrar o leer entradas en la base de datos.</p>
<p><br /><br /><br /></p>
<p dir="ltr">Este m&eacute;todo se a convertido en uno de los preferidos por los cibercriminales ya que los servidores de bases de datos son muy f&aacute;ciles de encontrar y f&aacute;cil de probar si son vulnerables al ataque, por que utilizan unos determinados patrones de ataque bien definidos.</p>
<p><br /><br /><br /></p>
<p dir="ltr"> </p>
<p dir="ltr"><strong>&iquest;C&oacute;mo identificar si somos vulnerables?</strong></p>
<p> </p>
<p dir="ltr">Las aplicaciones que se comunican mediante bases de datos normalmente no ofrecen el nivel de seguridad m&aacute;s indicado principalmente las que utilizan</p>
<p dir="ltr">SQL. Son altamente conocidas la vulnerabilidades de este lenguaje y para conocer si una aplicaci&oacute;n es vulnerable un atacante lo que suele utilizar son los mensajes de error, tan solo con introducir una comilla en la URL podemos saber que lenguaje de base de datos utiliza la aplicaci&oacute;n.</p>
<p> </p>
<p dir="ltr"><strong>[Nombre].es /script.php?id = 10&rsquo;</strong></p>
<p> </p>
<p dir="ltr">Esta manipulaci&oacute;n podr&iacute;a darnos como error lo siguiente:</p>
<p><br /><br /></p>
<p dir="ltr">Lo que nos dejar&iacute;a al descubierto el tipo de base de datos que utiliza la aplicaci&oacute;n.</p>
<p><br /><br /><br /></p>
<p dir="ltr"><strong>&iquest;C&oacute;mo ser&iacute;a un ataque?</strong></p>
<p> </p>
<p dir="ltr">El ataque m&aacute;s simple que se puede realizar es en la pantalla de inicio de una aplicaci&oacute;n donde se nos pide que nos autentiquemos podemos intentar introducir password&rsquo; OR 1= &lsquo;1 lo que podr&iacute;a dar lugar a la siguiente consulta:</p>
<p><br /><br /></p>
<p dir="ltr">Como la anterior sentencia es siempre verdadera nos dar&iacute;a acceso a los datos de usuario de la aplicaci&oacute;n, despu&eacute;s de dicho acceso el atacante se registrar&iacute;a como administrador pudiendo realizar cualquier tipo de operaci&oacute;n en la base de datos.</p>
<p> </p>
<p dir="ltr">Otra forma de ataque ser&iacute;a aprovech&aacute;ndose de las consultas que se realizan utilizando el ID de usuario, el cual es un m&eacute;todo habitual en las aplicaciones WEB pero no a salvo de debilidades, un ID transmitido en una URL permite a un servidor saber la informaci&oacute;n que tiene que enviar pero tambi&eacute;n permite a un atacante utilizarlo en su beneficio, por ejemplo una URL que contenga un script en PHP como el siguiente:</p>
<p> </p>
<p dir="ltr"><strong>&lt;? php</strong></p>
<p> </p>
<p dir="ltr"><strong>$ id = $_REQUEST[ &lsquo;id&rsquo;];</strong></p>
<p> </p>
<p dir="ltr"><strong>$ resultado = mysql_query(&ldquo;SELECT * from tabla WHERE id = $id&rdquo;);</strong></p>
<p> </p>
<p dir="ltr"><strong>?&gt;</strong></p>
<p> </p>
<p dir="ltr"> </p>
<p dir="ltr">Una URL esperada podr&iacute;a ser &hellip;../script.php?id = 45 la cual se podr&iacute;a manipular cambiando el id y de esa manera poder acceder a otros datos de los diferentes usuarios contenidos en la tabla, o en el caso m&aacute;s grave realizar una consulta falsa del siguiente tipo:</p>
<p> </p>
<p dir="ltr"><strong>&hellip;.../script.php? Id = 45+or+1 = 1</strong></p>
<p> </p>
<p dir="ltr">Lo cual resultar&iacute;a verdadera en todos los casos devolviendo como resultado todas las entradas de la base de datos.</p>
<p> </p>
<p dir="ltr"><strong>&iquest;C&oacute;mo protegernos de la inyecci&oacute;n?</strong></p>
<p> </p>
<p dir="ltr">Existen m&uacute;ltiples formas de proteger las aplicaciones de estas vulnerabilidades, pero debemos de tener en cuenta todos los componentes entran en juego para el funcionamiento de las mismas, como son configuraci&oacute;n de servidores y otras partes de c&oacute;digo de la aplicaci&oacute;n. Sin entrar en detalle puesto que ser&iacute;a muy extenso el explicar cada una de ellas vamos a enumerar las m&aacute;s importantes:</p>
<p> </p>
<p dir="ltr">-Controlar las enumeraciones autom&aacute;ticas de las aplicaciones.</p>
<p> </p>
<p dir="ltr">-Bastionado completo del servidor.</p>
<p> </p>
<p dir="ltr">Pero lo m&aacute;s importante es asegurar la base de datos utilizando c&oacute;digo seguro como por ejemplo el siguiente c&oacute;digo:</p>
<p> </p>
<p dir="ltr"><strong>$query = &ldquo;SELECT * FROM usuarios</strong></p>
<p dir="ltr"> </p>
<p dir="ltr"><strong>WHERE nombreUsuario= &lsquo;&rdquo; . $_POST[&lsquo;nombreUsuario&rsquo;] . &ldquo;&rsquo;&rdquo;</strong></p>
<p> </p>
<p dir="ltr"><strong>AND contrase&ntilde;a = &lsquo;&rdquo; . $_POST[&lsquo;contrase&ntilde;a&rsquo;] . &ldquo;&rsquo;&rdquo;;</strong></p>
<p> </p>
<p dir="ltr">Lo implementamos a&ntilde;adi&eacute;ndole una funci&oacute;n quedando el c&oacute;digo como sigue:</p>
<p> </p>
<p dir="ltr"><strong>$query = &ldquo;SELECT * FROM usuarios</strong></p>
<p> </p>
<p dir="ltr"><strong>WHERE nombreUsuario=&lsquo;&rdquo;.mysql_real_escape_string($_POST[&lsquo;nombreUsuario&rsquo;]) .&rdquo;</strong></p>
<p> </p>
<p dir="ltr"><strong>AND contrase&ntilde;a=&rsquo;&rdquo; . mysql_real_escape_string($_POST[&lsquo;contrase&ntilde;a&rsquo;]). &ldquo;&rsquo;&rdquo;;</strong></p>
<p> </p>
<p dir="ltr"> </p>
<p> </p>
<p dir="ltr">De esta manera se cambian los caracteres vulnerables por una variante SQL m&aacute;s segura. Espero que todas estas recomendaciones te sean de utilidad.</p>
<p><br /><br /><br /></p>]]></content:encoded>
</item><item>
<title><![CDATA[MotionLayout y MotionScene: La simpleza de las animaciones cada vez más cerca #AndroidMeetsKotlin]]></title>
<link>https://betabeers.com/blog/motionlayout-motionscene-la-simpleza-las-animaciones-cada-vez-mas-cerca-androidmeetskotlin-380/</link>
<pubDate>Fri, 02 Nov 2018 09:31:38 +0000</pubDate>
<category>Colaboraciones</category>
<content:encoded><![CDATA[<h2 id="h.h0si7hbbwwts" class="c8"><img style="display: block; margin-left: auto; margin-right: auto;" src="http://betabeers.com/uploads/blog/20181102_image_1.png" alt="" width="600" height="293" /></h2>
<p class="c3"><span>En el </span><span class="c5"><a class="c9" href="https://www.google.com/url?q=https://betabeers.com/blog/animaciones-constraintlayout-transitionmanager-androidmeetskotlin-375/&amp;sa=D&amp;ust=1541147080347000">anterior post</a></span><span> hiciste uso de ConstraintLayout, ConstraintSet y TransitionManager para hacer animaciones con cierta complejidad en poco tiempo y muy importante, con muy poco c&oacute;digo. En esta ocasi&oacute;n vas a tratar con un nuevo componente de Google con el que podr&aacute;s hacer animaciones con la misma complejidad o m&aacute;s pero s&oacute;lo con XML. Este componente se llama </span><span class="c5"><a class="c9" href="https://www.google.com/url?q=https://developer.android.com/reference/android/support/constraint/motion/MotionLayout&amp;sa=D&amp;ust=1541147080347000">MotionLayout</a></span><span class="c2">, &iexcl;vamos all&aacute;!.</span></p>
<p class="c3 c6"> </p>
<h2 id="h.nx7rsh823hdq" class="c8"><span class="c0">&iquest;Qu&eacute; es MotionLayout?</span></h2>
<p class="c3"><span class="c2">Como su nombre indica, se trata de un layout que posiciona sus elementos de UI y permite que se &ldquo;muevan&rdquo;. Si ves la implementaci&oacute;n de su clase, puedes observar que extiende de ConstraintLayout y por tanto, se nutre de todas sus propiedades.</span></p>
<p class="c3 c6"> </p>
<p class="c3"><span class="c2">Ha sido creado con el fin de acabar con el c&oacute;digo de las animaciones (s&oacute;lo utiliza XML) y el costoso (y fe&iacute;simo) control de animaciones, en cierto modo, es una uni&oacute;n entre el framework de animaciones, TransitionManager y CoordinatorLayout. Lo primero que se te puede venir a la cabeza es, &iquest;CoordinatorLayout?, pues s&iacute;, este componente tiene una capa de animaciones por defecto basadas en la interacci&oacute;n del usuario (por ejemplo, un scroll).</span></p>
<p class="c3 c6"> </p>
<p class="c3"><span class="c2">Como se ha comentado anteriormente, s&oacute;lo precisa de XML por lo que tiene un car&aacute;cter completamente declarativo. El objetivo final de este componente ser&aacute; la creaci&oacute;n de las animaciones usando una herramienta integrada en Android Studio (vas a llorar de felicidad, y lo sabes), de este modo reduces en gran medida la complejidad a la hora de crearlas y mantenerlas.</span></p>
<p class="c3 c6"> </p>
<p class="c3"><strong><span class="c1 c14">&iexcl;&iexcl;Limitaci&oacute;n importante!!</span></strong></p>
<p class="c3"><span class="c2">Las animaciones s&oacute;lo ser&aacute;n aplicables a sus hijos directos y no a otros niveles de jerarqu&iacute;a.</span></p>
<h2 id="h.sdbjm4f43aqu" class="c8"><span class="c0">&iquest;Cu&aacute;ndo debo usarlo?</span></h2>
<p class="c3"><span class="c2">Se debe usar cuando necesites animaciones ante interacciones de los usuarios con los componentes de UI, por ejemplo, un swipe en cualquier direcci&oacute;n.</span></p>
<h2 id="h.9uh6fs6xj3gz" class="c8"><span class="c0">Dependencia</span></h2>
<p class="c3"><span class="c2">Necesitas a&ntilde;adir la dependencia de ConstraintLayout en su versi&oacute;n 2 alpha</span></p>
<p class="c3 c6"> </p>
<p class="c3"><img src="http://betabeers.com/uploads/blog/20181102_image_2.png" alt="" width="700" height="38" /></p>
<p class="c3 c6"> </p>
<h2 id="h.uerl5mut4jlf" class="c8"><span class="c0">&iquest;C&oacute;mo lo a&ntilde;ado al XML?</span></h2>
<p class="c3"><span class="c2">Simplemente usas:</span></p>
<p class="c3 c6"> </p>
<p class="c3"><img src="http://betabeers.com/uploads/blog/20181102_image_3.png" alt="" width="500" height="36" /></p>
<p class="c3 c6"> </p>
<p class="c3"><span class="c2">Es importante saber que MotionLayout se nutre de uno o varios MotionScene para realizar las animaciones. Adem&aacute;s, puesto que extiende ConstraintLayout, puedes hacer uso de todas sus propiedades y potencial.</span></p>
<h2 id="h.mfydg36jg0ix" class="c8"><span class="c0">&iquest;Qu&eacute; es un MotionScene?</span></h2>
<p class="c3"><span class="c2">Se trata del componente donde configuras tu animaci&oacute;n. Puede tener a su vez otros componentes como Transition, ConstraintSet, etc. Estar&aacute;n alojados en la carpeta &ldquo;res/xml&rdquo;.</span></p>
<h2 id="h.b8wemovrpxwq" class="c8"><span class="c0">&iquest;Necesitas saber algo nuevo de ConstraintSet?</span></h2>
<p class="c3"><span class="c2">No, se tratan de las mismas que usas con ConstraintLayout y sirven para encapsular todas las restricciones necesarias para posicionar un elemento respecto a otro. Puedes crear tantos ConstraintSet como necesites y luego cambiarlas por c&oacute;digo, esto provocar&aacute; animaciones y transiciones al actualizarse sus nuevas restricciones.</span></p>
<h2 id="h.fuw1b9db0ls6" class="c8"><span class="c0">Sin m&aacute;s, &iexcl;al turr&oacute;n!</span></h2>
<p class="c3"><span>Ya conoces el escenario y tienes unas ideas b&aacute;sicas para poder crear tu primera animaci&oacute;n, para ello vas a implementar la </span><span class="c5"><a class="c9" href="https://www.google.com/url?q=https://github.com/franlopjur/MotionLayout/blob/master/motionlayout_animacion.gif&amp;sa=D&amp;ust=1541147080350000">siguiente</a></span><span class="c2">.</span></p>
<p class="c3 c6"> </p>
<h2 id="h.wtw9anbskk51" class="c8"><span class="c0">Define tu primer layout usando MotionLayout.</span></h2>
<p class="c3"><span class="c2">Seg&uacute;n has podido ver a lo largo del post, este layout es exactamente igual que ConstraintLayout pero con varias propiedades m&aacute;s, en este caso usar&aacute;s:</span></p>
<ul class="c7 lst-kix_2ir4yfyljvsc-0 start">
<li class="c3 c11"><strong><span class="c1">applyMotionScene</span></strong><span class="c2">. Por defecto est&aacute; a true, indicar&aacute; si tienes o no que hacer la escena especificada.</span></li>
<li class="c3 c11"><strong><span class="c1">layoutDescription</span></strong><span class="c2">. Establece la escena para tu animaci&oacute;n desde el recurso XML. (@xml/animation_motion_scene)</span></li>
<li class="c3 c11"><strong><span class="c1">showPaths</span></strong><span class="c2">. Muestra la ruta que seguir&aacute; la animaci&oacute;n. Como es l&oacute;gico, s&oacute;lo debes establecerlo a true en debug.</span></li>
</ul>
<p class="c3 c6"> </p>
<p class="c3"><span class="c2">En este caso no necesitas definir ninguna constraint en este layout puesto que ir&aacute;n definidas por completo en la escena que configurar&aacute; la animaci&oacute;n.</span></p>
<p class="c3"><span>El c&oacute;digo XML lo encuentras  </span><span class="c5"><a class="c9" href="https://www.google.com/url?q=https://github.com/franlopjur/MotionLayout/blob/master/activity_home.xml&amp;sa=D&amp;ust=1541147080352000">aqu&iacute;</a></span><span class="c2">.</span></p>
<h2 id="h.825b44ta7ov1" class="c8"><span class="c0">Crea tu primer MotionScene</span></h2>
<p class="c3"><img src="http://betabeers.com/uploads/blog/20181102_image_4.png" alt="" width="600" height="193" /></p>
<p class="c3 c6"> </p>
<p class="c3"><span class="c2">Tendr&aacute;s 3 grandes componentes:</span></p>
<h3 id="h.3taxzo5b6u38" class="c12"><span class="c10">Transition. </span></h3>
<p class="c3"><span class="c2">Definir&aacute; la animaci&oacute;n y bajo qu&eacute; acci&oacute;n se activar&aacute;. Las propiedades que vas a usar en este post son:</span></p>
<ul class="c7 lst-kix_u9vfgjptbin6-0 start">
<li class="c3 c11"><strong><span class="c1">constraintSetEnd</span></strong><span class="c2">. Define las constraints para el final de la animaci&oacute;n (@+id/motion_constraintset__end).</span></li>
<li class="c3 c11"><strong><span class="c1">constraintSetStart</span></strong><span class="c2">. Define las constraints para el inicio de la animaci&oacute;n (@+id/motion_constraintset__start).</span></li>
<li class="c3 c11"><strong><span class="c1">duration</span></strong><span class="c2">. Establece la duraci&oacute;n m&aacute;xima de la animaci&oacute;n (700 milisegundos en tu caso).</span></li>
</ul>
<p class="c3 c6"> </p>
<p class="c3">Adem&aacute;s de las propiedades, debes crear tres componentes &ldquo;onSwipe&rdquo; (uno por cada vista a animar). &Eacute;ste ser&aacute; el encargado de configurar c&oacute;mo se animar&aacute;n nuestras vistas. En este ejemplo basta con que lo definas con las siguientes propiedades:</p>
<ul class="c7 lst-kix_cuj79jwgdkzm-0 start">
<li class="c3 c11"><strong>dragDirection</strong>. Establece desde qu&eacute; lado se hace el desplazamiento. Para tu ejemplo ser&aacute; "dragUp". Los valores posibles son: dragUp | dragDown | dragLeft | dragRight.</li>
<li class="c3 c11"><strong><span class="c1" data-mce-mark="1">touchAnchorId</span></strong><span class="c2" data-mce-mark="1">. Establece el id del objeto al que se le aplicar&aacute; la propiedad &ldquo;touchAnchorSide&rdquo;.</span></li>
<li class="c3 c11"><strong><span class="c1" data-mce-mark="1">touchAnchorSide</span></strong><span data-mce-mark="1">. Establece el lado del objeto a mover. Para tu ejemplo ser&aacute; "top". Valores permitidos: </span><span class="c13" data-mce-mark="1">top | left | right | bottom.</span></li>
</ul>
<h3 id="h.wgeew92mc56s" class="c12"><span class="c10">ConstraintSet de comienzo.</span></h3>
<p class="c3"><span class="c2">Define las constraints iniciales a las vistas, en tu caso las constraints de inicio har&aacute;n que las tres im&aacute;genes est&eacute;n en la parte superior.</span></p>
<p class="c3"><img src="http://betabeers.com/uploads/blog/20181102_constraintset_start.PNG" alt="" width="172" height="350" /></p>
<h3 id="h.7459g45ffy3w" class="c12"><span class="c10">ConstraintSet de fin.</span></h3>
<p class="c3"><span class="c2">Define las constraints finales a los mismos componentes, para este ejemplo, las tres im&aacute;genes se situar&aacute;n verticalmente y en el centro.</span></p>
<p class="c3 c6"> <img src="http://betabeers.com/uploads/blog/20181102_constraintset_end.PNG" alt="" width="169" height="350" /></p>
<p class="c3 c6"> </p>
<p class="c3"><span>El c&oacute;digo de la escena ser&iacute;a el </span><span class="c5"><a class="c9" href="https://www.google.com/url?q=https://github.com/franlopjur/MotionLayout/blob/master/animation_motion_scene.xml&amp;sa=D&amp;ust=1541147080354000">siguiente</a></span><span class="c2">.</span></p>
<p class="c3 c6"> </p>
<h2 id="h.7i18gftmyzt5" class="c8"><span class="c0">Vincula el layout con nuestra escena.</span></h2>
<p class="c3"><span>Haciendo uso de la propiedad &ldquo;</span><span class="c1">layoutDescription</span><span class="c2">&rdquo; podr&aacute;s establecer la escena a tu layout, despu&eacute;s de esto, no tienes que hacer nada m&aacute;s.</span></p>
<p class="c3 c6"> </p>
<p class="c3"><img src="http://betabeers.com/uploads/blog/20181102_image_5.png" alt="" width="600" height="219" /></p>
<p class="c3 c6"> </p>
<p class="c3"><span>Ejecuta el c&oacute;digo y ver&aacute;s la </span><span class="c5"><a class="c9" href="https://www.google.com/url?q=https://youtu.be/7TBRAxYwpis&amp;sa=D&amp;ust=1541147080355000">siguiente animaci&oacute;n</a></span><span class="c2"> (con el path visible).</span></p>
<h2 id="h.lrygc0uivm9h" class="c8"><span class="c0">Pero&hellip; &iquest;No hay c&oacute;digo en la vista o en la Activity / Fragment?</span></h2>
<p class="c3"><span class="c2">No, esta es la magia de MotionLayout, no necesitas nada de c&oacute;digo Java o Kotlin para realizar tus animaciones.</span></p>
<h2 id="h.9w7au1deqyzc" class="c8"><span class="c0">Conclusi&oacute;n.</span></h2>
<p class="c3"><span class="c2">Este nuevo componente te proporciona una nueva forma de enfocar nuestras animaciones donde s&oacute;lo importa las definiciones por XML. El &uacute;nico problema es que todav&iacute;a est&aacute; en alpha y tendr&aacute;s que esperar un poco m&aacute;s para que finalmente saquen la primera release. Espero que te haya gustado.</span></p>
<p class="c3 c6"> </p>
<p class="c3 c6"> </p>
<p class="c3 c6"> </p>
<p class="c3 c6"> </p>
<p class="c3 c6"> </p>
<p class="c3 c6"> </p>
<p class="c3 c6"> </p>
<p class="c3 c6"> </p>
<p class="c3 c6"> </p>
<p class="c3 c6"> </p>
<p class="c3 c6"> </p>
<p class="c3 c6"> </p>
<p class="c3 c6"> </p>
<p class="c3 c6"> </p>]]></content:encoded>
</item><item>
<title><![CDATA[Betapod 0.3.001 - Terceras partes nunca fueron buenas]]></title>
<link>https://betabeers.com/blog/betapod-03001-terceras-partes-nunca-fueron-buenas-379/</link>
<pubDate>Tue, 30 Oct 2018 22:20:33 +0000</pubDate>
<category>Podcast</category>
<content:encoded><![CDATA[<p><img style="display: block; margin-left: auto; margin-right: auto;" title="Betabeers podcast" src="http://betabeers.com/uploads/blog/20161228_Cartel_Betabeers_Podcast.png" alt="Betabeers podcast" width="600" height="200" /></p>
<p dir="ltr"><span>&iexcl;El podcast de Betabeers ha vuelto despu&eacute;s de una larga espera! Como antesala de una nueva tercera temporada con varios cambios, hemos grabado un peque&ntilde;o resumen de las noticias m&aacute;s relevantes de los &uacute;ltimos meses a modo de debate, adem&aacute;s de un nuevo espacio en el podcast: la ruleta de las noticias.</span></p>
<p><iframe src="https://www.ivoox.com/player_ej_29720615_4_1.html?c1=ff6600" frameborder="0" style="border: 1px solid #EEE; box-sizing: border-box; width: 100%;" scrolling="no" width="320" height="200"></iframe></p>
<p dir="ltr"><span>Por no entrar en profundidad en todas las noticias desde la publicaci&oacute;n del &uacute;ltimo programa, en este primer programa de la temporada hicimos una selecci&oacute;n las noticias m&aacute;s destacables, como han sido la compra de Github por parte de Microsoft, el cambio de licencia de Java 11, la liberaci&oacute;n de m&uacute;ltiples patentes de Microsoft a la Open Invention Network o el abandono de React Native por parte de Airbnb.<br /></span></p>
<p dir="ltr"><span>Despu&eacute;s de este repaso introducimos una nueva secci&oacute;n, en la cual seleccionamos aleatoriamente noticias del <a href="https://www.reddit.com/r/programming/">subreddit de programming</a> y las comentamos, debatimos y, en varias ocasiones, nos vamos por las ramas. Las noticias seleccionadas para este primer programa han tocado temas c&oacute;mo el ya mencionado <a href="https://reddit.app.link/VCvkU4L8dR">cambio de licencia de Java</a>, <a href="https://reddit.app.link/TryceuF8dR">cu&aacute;l es la caracter&iacute;stica m&aacute;s destacable de Lisp</a>, <a href="https://reddit.app.link/d1Z0zLW8dR">las diferencias entre el cifrado de GMail y Whatsapp</a>, <a href="https://reddit.app.link/nlmWkXS8dR">una herramienta para optimizar el consumo de bater&iacute;a de las aplicaciones</a> y tratando tem&aacute;s tan aleatorios c&oacute;mo un <a href="https://reddit.app.link/LwFUqmQ8dR">Code School celebrado en Frisco</a>.</span></p>
<h3 dir="ltr"><span>Agenda y otras noticias Betabeers</span></h3>
<p>Y con un breve resumen de los pr&oacute;ximos eventos del sector, con un fin de a&ntilde;o trepidante entre <a href="https://madrid2018.codemotionworld.com/es/">Codemotion</a>, <a href="https://2018.commit-conf.com/">Commit Conf</a> y <a href="http://www.lambda.world/">LambdaWorld</a> entre otros muchos, cerramos el primer programa invitandoos a visitar la web y conocer estos y otros eventos a los que asistir por toda Espa&ntilde;a y parte del mundo, as&iacute; como a proponer temas y darnos tu opini&oacute;n y sugerencias para hacer que esta temporada sea la mejor realizada hasta el momento. &iexcl;Nos escuchamos en el pr&oacute;ximo programa!</p>]]></content:encoded>
</item><item>
<title><![CDATA[Cómo crear shortcodes personalizados en WordPress]]></title>
<link>https://betabeers.com/blog/como-crear-shortcodes-personalizados-wordpress-378/</link>
<pubDate>Fri, 26 Oct 2018 14:38:29 +0000</pubDate>
<category>Colaboraciones</category>
<content:encoded><![CDATA[<p>Los shortcodes son un m&eacute;todo m&aacute;s que interesante mediante el cual podemos incluir elementos espec&iacute;ficos en nuestros <strong>contenidos publicados a trav&eacute;s de WordPress</strong>. A nivel de usuario final, los shortcodes nos permiten, a trav&eacute;s de combinaciones de atajos de texto poder insertar contenido que se imprimir&aacute; en el <em>frontend</em> de nuestro sitio web <strong>sin necesidad de intervenir en el c&oacute;digo </strong>de los temas o plugins instalados.</p>
<p><img style="display: block; margin-left: auto; margin-right: auto;" title="Shortcodes WordPress Betabeers" src="http://betabeers.com/uploads/blog/20181026_shortcodes-wordpress-betabeers-blog.jpg" alt="Shortcodes WordPress Betabeers" width="600" height="400" /></p>
<p>La <strong>API de Shortcodes para WordPress</strong> nos permite crear de una manera sencilla este tipo de atajos de c&oacute;digo. A nivel de desarrollo, podemos construir instrucciones sencillas para el usuario en el caso de que tenga que introducir determinados valores en el sitio web, que de otro modo ser&iacute;a m&aacute;s complejo, o en ocasiones, demasiado repetitivo.</p>
<p>En este art&iacute;culo hablaremos un poco m&aacute;s del funcionamiento en s&iacute; mismo y podremos comprobar c&oacute;mo podemos <strong>crear nuestros propios shortcodes</strong> a trav&eacute;s de una serie de pasos muy sencillos.</p>
<h2>API de Shortcodes para WordPress</h2>
<p>La <a href="https://codex.wordpress.org/Shortcode" target="_blank">API de Shortcodes para WordPress</a> es una herramienta para desarrolladores que nos permite generar, mediante fragmentos de texto entre corchetes, un c&oacute;digo completo que se imprimir&aacute; en consecuencia. El propio WordPress dispone de algunos shortcodes creados por defecto que a buen seguro te van a sonar.</p>
<p>[audio] [caption] [embed] [gallery] [playlist] [video]</p>
<p>Cuando introducimos alguno de estos shortcodes en nuestro editor de texto y a&ntilde;adimos los correspondientes par&aacute;metros podremos mostrar cualquier tipo de informaci&oacute;n sin necesidad de depender de la actualizaci&oacute;n de c&oacute;digo HTML o PHP para tal fin.</p>
<p>Esta funcionalidad a&ntilde;adida supone una mejora en el mantenimiento de contenidos de un sitio web y una mayor facilidad de uso para un cliente final encargado de gestionar publicaciones.</p>
<h2>C&oacute;mo crear un Shortcode para WordPress</h2>
<p>Despu&eacute;s de tener una visi&oacute;n general de la herramienta y haber entendido su funcionamiento, vamos a proceder a crear un ejemplo sencillo pero que nos dar&aacute; una buena aproximaci&oacute;n y nos permitir&aacute; poner en pr&aacute;ctica esta funcionalidad para WordPress.</p>
<p>En este ejemplo crearemos un shortcode que nos permitir&aacute; a&ntilde;adir una imagen por defecto a modo de placeholder en cualquier post en tan solo unos segundos. Es un proceso sencillo, pero que si lo complementamos con cierta creatividad nos podr&aacute; dar juego para nuestros desarrollos permitiendo a&ntilde;adir contenido de manera sencilla a trav&eacute;s del editor de texto de WordPress.</p>
<p>Los pasos que deberemos llevar a cabo son los que exponemos a continuaci&oacute;n.</p>
<h3>Crear la funci&oacute;n del shortcode</h3>
<p>Tendremos que crear la funci&oacute;n para el shortcode, y en este caso, WordPress nos da los par&aacute;metros necesarios para utilizar el hook <a href="https://codex.wordpress.org/Function_Reference/add_shortcode" target="_blank">add_shortcode</a>.</p>
<pre>add_shortcode( 'dummyimage', 'dummyimage_shortcode_function' );
</pre>
<p><a href="https://gist.github.com/fernandiez/8fff94b21ebaf72f4fdd82463541829d#file-gistfile1-txt" target="_blank">Enlace a gist</a></p>
<p><a href="https://gist.github.com/fernandiez/8fff94b21ebaf72f4fdd82463541829d#file-gistfile1-txt" target="_blank">En este caso, dummyimage es el valor que deberemos utilizar para para llamar al shortcode desde el editor, y la funci&oacute;n que nos permitir&aacute; definir el contenido que debemos imprimir.</a></p>
<h3>A&ntilde;adir los atributos variables</h3>
<p>A&ntilde;adiremos a nuestra funci&oacute;n los elementos necesarios para definir valores como el ancho de la imagen, la altura, el color de fondo y el color del texto incluido. Tambi&eacute;n podemos a&ntilde;adir unos valores por defecto, que se utilizar&aacute;n en el caso de utilizar el shortcode sin indicar ning&uacute;n tipo de par&aacute;metro.</p>
<p>function dummyimage_shortcode_function( $atts ) {</p>
<pre>  // Attributes
$atts = shortcode_atts(
array(
'width' =&gt; '600',
'height' =&gt; '300',
'background' =&gt; 'eeeeee',
'text' =&gt; 'ffffff',
),
$atts
);

</pre>
<p><a href="https://gist.github.com/fernandiez/8fff94b21ebaf72f4fdd82463541829d#file-gistfile2-txt" target="_blank">Enlace a gist</a></p>
<h3>C&oacute;digo a implementar</h3>
<p>Por &uacute;ltimo deberemos a&ntilde;adir el c&oacute;digo que se va a imprimir, siendo este caso la inserci&oacute;n de una imagen. En el caso que nos ocupa utilizaremos el servicio gratuito para generar im&aacute;genes de <a href="https://dummyimage.com/" target="_blank">dummyimage.com</a>.</p>
<p>return '<img src="https://dummyimage.com/'. $value['width'] . '/'. $value['height'] . '/'. $value['backgound'] . '/'. $value['text'] . '.jpg" alt="" />';</p>
<p><a href="https://gist.github.com/fernandiez/8fff94b21ebaf72f4fdd82463541829d#file-gistfile3-txt" target="_blank">Enlace a gist</a></p>
<h3>Funci&oacute;n completa</h3>
<p>Si recopilamos las instrucciones dadas tendremos el siguiente c&oacute;digo que podremos a&ntilde;adir a nuestro archivo de funciones, functions.php, o en nuestro tema en desarrollo o plugin instalado.</p>
<pre>// A&ntilde;adir Shortcode
function dummyimage_shortcode_function( $atts ) {

// Atributos
$atts = shortcode_atts(
array(
'width' =&gt; '600',
'height' =&gt; '300',
'background' =&gt; 'eeeeee',
'text' =&gt; 'ffffff',
),
$atts
);

return '</pre>
<p><img src="https://dummyimage.com/'. $value['width'] . '/'. $value['height'] . '/'. $value['backgound'] . '/'. $value['text'] . '.jpg" alt="" /></p>
<pre>';</pre>
<pre>}<br />
add_shortcode( 'dummyimage', 'dummyimage_shortcode_function' );
</pre>
<p><a href="https://gist.github.com/fernandiez/8fff94b21ebaf72f4fdd82463541829d#file-functions-php" target="_blank">Enlace a gist</a></p>
<p>Si en este caso usamos la llamada al shortcode tal cual, lo que conseguiremos es imprimir la imagen por defecto que necesitamos tomando los valores que hayamos indicado en la creaci&oacute;n del shortcode. Tendr&iacute;amos que utilizar el shortcode sin ning&uacute;n tipo de par&aacute;metro.</p>
<p>[dummyimage]</p>
<p> </p>
<p><img src="https://dummyimage.com/600x300/eeeeee/ffffff.jpg" alt="" width="600" height="300" /></p>
<p>Si lo que buscamos es personalizar los par&aacute;metros de salida de nuestra imagen a modo de placeholder, es necesario que indiquemos esas variables del siguiente modo.</p>
<p>[dummyimage width="300" height="200" background="000000" text="ffffff"]</p>
<p><img src="https://dummyimage.com/200x200/000000/ffffff.jpg" alt="" width="200" height="200" /></p>
<h2>Conclusiones</h2>
<p>Si realizar mejoras en nuestros desarrollos, uno de los objetivos que debemos tener en cuenta es el uso que van a dar a la herramienta nuestros clientes o encargados de la edici&oacute;n de contenidos.</p>
<p>Todo aquello que nos permita mejorar su flujo de trabajo podr&aacute; considerarse un &eacute;xito en el remate de un proyecto y tendr&aacute; consecuencias en una mejor adaptaci&oacute;n a la publicaci&oacute;n de contenidos en WordPress.</p>]]></content:encoded>
</item><item>
<title><![CDATA[Portainer.io: monitorea y administra Docker de manera sencilla e intuitiva]]></title>
<link>https://betabeers.com/blog/portainerio-monitorea-administra-docker-manera-sencilla-e-intuitiva-377/</link>
<pubDate>Fri, 19 Oct 2018 10:11:32 +0000</pubDate>
<category>Colaboraciones</category>
<content:encoded><![CDATA[<h3 style="text-align: center;"><em><a href="https://kitematic.com/"><img src="http://betabeers.com/uploads/blog/20181019_portainer.io-logo.jpg" alt="Portainer.io logo" width="400" height="174" /></a></em></h3>
<p style="text-align: left;"><em>Este art&iacute;culo ha sido escrito por <a href="https://twitter.com/davitol" target="_blank">David Toledo</a> y publicado originalmente en el blog de <a href="https://solidgeargroup.com/portainer-io-monitorea-y-administra-docker-de-manera-sencilla-e-intuitiva?lang=es" target="_blank">Solid GEAR</a>.</em></p>
<h3>&iquest;Que es Portainer.io?</h3>
<p>Portainer es una herramienta web open-source la cual se ejecuta ella misma como un container, por tanto deberemos tener Docker instalado. Esta aplicaci&oacute;n nos va a permitir gestionar de forma muy f&aacute;cil e intuitiva nuestros contenedores Docker a trav&eacute;s de una interfaz gr&aacute;fica.</p>
<h3> </h3>
<h3>&iquest;Por qu&eacute; usar Portainer.io?</h3>
<p>Para las tareas de administraci&oacute;n de contenedores Docker, a veces la l&iacute;nea de comandos puede hacerse algo dura para realizar ciertas acciones. A trav&eacute;s de una interfaz web, un administrador puede tener una visi&oacute;n global m&aacute;s clara de los contenedores que est&aacute; ejecutando y facilitar su gesti&oacute;n.</p>
<p><a href="https://kitematic.com/">Kitematic</a> es la GUI por defecto que viene con la versi&oacute;n de Docker para Windows y Mac. Seguramente muchos de vosotros ya la conoc&eacute;is y hab&eacute;is tenido contacto con ella, pero en algunas circunstancias puede quedar algo corta. <a href="https://portainer.io/">Portainer.io</a> va un paso m&aacute;s all&aacute; a la hora de darnos funcionalidad para gestionar nuestro Docker a trav&eacute;s de una interfaz.</p>
<h3> </h3>
<h3>&iexcl;Vamos a instalarlo!</h3>
<p>Portainer est&aacute; disponible para los sistemas operativos Windows, Linux y Mac. Yo lo he probado con Mac solamente.</p>
<p>La instalaci&oacute;n es bastante sencilla,</p>
<p><code>docker volume create portainer_data</code></p>
<p><code>docker run -d -p 9000:9000 -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer</code></p>
<p>Lo que hace es escuchar en el puerto 9000. All&iacute; es donde lo tendremos arrancado. Por tanto para acceder a &eacute;l, simplemente debemos ir a <code>http://localhost:9000</code> en nuestro navegador. <code>portainer/portainer</code>, es el repositorio de DockerHub de donde descargamos la imagen de Portainer.</p>
<p>Os preguntar&aacute; que pong&aacute;is un usuario y una contrase&ntilde;a</p>
<p> </p>
<p style="text-align: center;"><img class="size-full wp-image-6596 aligncenter" src="https://solidgeargroup.com/wp-content/uploads/2018/10/Screen-Shot-2018-10-02-at-18.37.41.png" alt="admin-pwd" width="571" height="362" /></p>
<p> </p>
<p> </p>
<p>Un vez creado el usuario administrador con una contrase&ntilde;a v&aacute;lida para la pol&iacute;tica de password, pasar&eacute;is a la siguiente ventana donde nos preguntar&aacute; si queremos levantarlo en local o en remoto, en mi caso he seleccionado en local para as&iacute; manejar los containers que tengo ejecutando en mi propio ordenador.</p>
<p> </p>
<p style="text-align: center;"><img class="size-full wp-image-6593 aligncenter" src="https://solidgeargroup.com/wp-content/uploads/2018/10/Screen-Shot-2018-10-02-at-18.34.53.png" alt="" width="840" height="410" /></p>
<p> </p>
<h3>Interfaz</h3>
<p>La primera secci&oacute;n, llamada <strong>Dashboard</strong>, es la p&aacute;gina principal de nuestra instancia de Portainer. En ella se muestra un resumen de manera visual de nuestro sistema de Docker: n&uacute;mero total de contenedores, de im&aacute;genes, redes o vol&uacute;menes. Esta secci&oacute;n es de gran utilidad para mostrarnos de manera global cual es el estado actual de Docker en nuestra m&aacute;quina.</p>
<p> </p>
<p style="text-align: center;"><img class="wp-image-6599 aligncenter" src="https://solidgeargroup.com/wp-content/uploads/2018/10/Screen-Shot-2018-10-02-at-18.42.23.png" alt="" width="1100" height="274" /></p>
<p> </p>
<p>El men&uacute; de <strong>Containers</strong>, nos mostrar&aacute; la lista de todos nuestros contenedores, y podremos ejecutar a golpe de click varias de las t&iacute;picas instrucciones que solemos ejecutar a trav&eacute;s de la l&iacute;nea de comandos, como arrancarlos, pararlos o eliminarlos. Tambi&eacute;n podemos ver detalles del propio contenedor. Si hacemos click en el nombre de un contenedor, entonces podemos conocer la informaci&oacute;n del mismo</p>
<p style="text-align: center;"><img class="wp-image-6589 aligncenter" src="https://solidgeargroup.com/wp-content/uploads/2018/10/Screen-Shot-2018-10-02-at-18.05.39.png" alt="" width="1100" height="302" /></p>
<p> </p>
<p> </p>
<p>Esa pantalla llamada <strong>Container details</strong> nos permite:</p>
<p>&ndash; Todas las operaciones mas habituales como parar, pausar, matar o borrar el contenedor<br />&ndash; Ver informaciones del contenedor (<code>docker inspect</code>)<br />&ndash; Crear una imagen nueva desde el mismo contenedor y a&ntilde;adirla a un registro (<code>docker commit</code>)<br />&ndash; Ver los logs del contenedor (<code>docker logs</code>)<br />&ndash; Ver las estad&iacute;sticas del contenedor (<code>docker stats</code>)<br />&ndash; Entrar en el contenedor pudiendo elegir el shell o el usuario (<code>docker exec</code>)<br />&ndash; Conectar/Desconectar el contenedor con una red (<code>docker network connect</code>)</p>
<p> </p>
<p>La secci&oacute;n de <strong>Images</strong> corresponder&iacute;a, si estuviesemos trabajando con el terminal, al comando <code>docker images</code> .</p>
<p style="text-align: center;"><img class="aligncenter wp-image-6567" src="https://solidgeargroup.com/wp-content/uploads/2018/10/Screen-Shot-2018-10-02-at-16.14.24.png" alt="" width="1100" height="460" /></p>
<p> </p>
<p>La interfaz web de Portainer para la seccion de im&aacute;genes nos ofrece una serie de ventajas frente a la l&iacute;nea de comandos como ejemplo</p>
<ul>
<li>Ordenar la lista por tags</li>
<li>Ordenar la lista por tama&ntilde;o</li>
<li>Selecci&oacute;n de una / varias im&aacute;genes a trav&eacute;s de checkboxes</li>
<li>Tag unused para mostrarnos aquellas im&aacute;genes que actualmente no estamos usando.</li>
</ul>
<p> </p>
<p>En la pantalla de <strong>Networks</strong>, tenemos la posibilidad de ver las redes que ya tenemos creadas. Tambi&eacute;n podemos eliminarlas o a&ntilde;adir a trav&eacute;s de la interfaz una nueva red.</p>
<p> </p>
<p style="text-align: center;"><img class="wp-image-6613 aligncenter" src="https://solidgeargroup.com/wp-content/uploads/2018/10/Screen-Shot-2018-10-05-at-13.04.35.png" alt="" width="1100" height="348" /></p>
<p> </p>
<p>En V<strong>olumes</strong> veremos los directorios del sistema que usa docker (<code>docker volume ls</code>)</p>
<p>Y para terminar, existe una secci&oacute;n llamada <strong>App Templates</strong>, la cual no he usado a&uacute;n en profundidad pero me ha parecido muy interesante. Hay gran cantidad de plantillas disponibles para descargar e instalar. En mi caso por ejemplo necesitaba para unas pruebas un contenedor de Scality S3 y me result&oacute; muy &uacute;til dicha plantilla para conseguir montarlo.</p>
<p> </p>
<p style="text-align: center;"><img class="aligncenter wp-image-6620" src="https://solidgeargroup.com/wp-content/uploads/2018/10/Screen-Shot-2018-10-05-at-13.36.51.png" alt="Templates" width="1100" height="676" /></p>
<p> </p>
<p>Espero que tras leer este art&iacute;culo te animes a instalar y probar Portainer. Si quieres m&aacute;s informaci&oacute;n sobre esta herramienta y ampliar tus conocimientos, puedes consultar aqu&iacute; su <a href="https://portainer.readthedocs.io/en/stable/">documentaci&oacute;n</a>.</p>]]></content:encoded>
</item><item>
<title><![CDATA[Todas las novedades de la web de Betabeers]]></title>
<link>https://betabeers.com/blog/todas-las-novedades-la-web-betabeers-376/</link>
<pubDate>Tue, 16 Oct 2018 11:00:17 +0000</pubDate>
<category>Noticias</category>
<content:encoded><![CDATA[<p dir="ltr"><span><img style="display: block; margin-left: auto; margin-right: auto;" src="http://betabeers.com/uploads/blog/20181016_BetaberesNovedadesWeb.jpg" alt="" width="600" height="191" /></span></p>
<p dir="ltr"> </p>
<p dir="ltr"><span>La tecnolog&iacute;a est&aacute; en constante evoluci&oacute;n. Las comunidades se adaptan r&aacute;pidamente a los cambios. Y la web de Betabeers ha ido evolucionando para ajustar a estos cambios y demandas del sector.</span></p>
<p dir="ltr"><span>Seguimos siendo una comunidad tecnol&oacute;gica. Los encuentros mensuales se mantienen como nuestra referencia, punto de encuentro de los desarrolladores, emprendedores e interesados en la tecnolog&iacute;a. Y la web sigue siendo nuestro epicentro. Al calor de los encuentros, han surgido diferentes &aacute;reas complementarias, como el foro, empleo o los  cursos.</span></p>
<p dir="ltr"> </p>
<p dir="ltr"><strong>Ofertas de empleo mejoradas</strong></p>
<p dir="ltr"><span>Los profesionales del desarrollo nos movemos en un entorno altamente demandante y requerimos ofertas con ventajas competitivas, transparentes y realistas. En nuestro objetivo por contribuir a facilitar la b&uacute;squeda de la mejor oferta, la secci&oacute;n de empleo ha evolucionado para los profesionales podamos conocer mejor cada oferta que se publica y la empresa que la respalda. Toda la informaci&oacute;n que podamos proporcionarte ser&aacute; de valor para tomar una decisi&oacute;n sobre tu futuro. </span></p>
<p><span><span> </span></span></p>
<p dir="ltr"><strong>Una newsletter con la informaci&oacute;n que m&aacute;s te interesa</strong></p>
<p dir="ltr">La newsletter de Betabeers tiene ya 8 a&ntilde;os de vida. Resumimos la agenda de pr&oacute;ximos eventos a tu alrededor, &uacute;ltimas noticias del blog, as&iacute; como ofertas de empleo que se adaptan a tu perfil. Ahora la hemos personalizado a&uacute;n m&aacute;s para cada perfil. Por cierto, &iquest;hace tiempo que no actualizas <a href="../../user/edit/">tu perfil de Betabeers</a>? &iexcl;Actual&iacute;zalo cuanto antes!</p>
<p dir="ltr">Y si a&uacute;n no eres parte de la comunidad de suscriptores, &eacute;ste es el momento para que no pierdas detalle de todo lo que ocurre en el sector digital. &iexcl;<a href="../../user/alerts/">Suscr&iacute;bete a la newsletter</a> de Betabeers!</p>
<p dir="ltr"> </p>
<p dir="ltr"><span>Se avecinan m&aacute;s cambios en la web, para hacerla m&aacute;s responsive y adaptarla a las demandas del sector tecnol&oacute;gico. Si tienes alguna sugerencia que crees que podemos implementar, alg&uacute;n error que no hemos detectado o alguna idea disruptiva, ser&aacute; m&aacute;s que bienvenida en los comentarios.</span></p>
<p><span> </span></p>]]></content:encoded>
</item><item>
<title><![CDATA[Animaciones con ConstraintLayout y TransitionManager #AndroidMeetsKotlin]]></title>
<link>https://betabeers.com/blog/animaciones-constraintlayout-transitionmanager-androidmeetskotlin-375/</link>
<pubDate>Fri, 05 Oct 2018 01:03:13 +0000</pubDate>
<category>Colaboraciones</category>
<content:encoded><![CDATA[<p><img style="display: block; margin-left: auto; margin-right: auto;" src="http://betabeers.com/uploads/blog/20181005_header.PNG" alt="" width="400" height="325" /></p>
<p class="c5">Las animaciones siempre nos ha tra&iacute;do de cabeza a pr&aacute;cticamente la mayor&iacute;a de desarrolladores en Android. Cada vez son m&aacute;s las facilidades que nos proporciona el SDK para conseguir que nuestras animaciones no acaben en un c&oacute;digo ilegible o demasiado tedioso.</p>
<p class="c5">En este caso, ConstrantLayout junto con ConstraintSet y TransitionManager nos proporcionan una herramienta con la que podremos crear animaciones sin mucha complicaci&oacute;n y realmente fluidas.</p>
<h2 id="h.j4i3w29wpcue" class="c15">ConstraintSet</h2>
<p class="c5">Permite definir program&aacute;ticamente un conjunto de restricciones aplicables a nuestras vistas. Podemos crear, guardar o clonar restricciones y luego aplicarlas a un ConstraintLayout. Existen distintas formas de crear/inicializar esta clase:</p>
<ul class="c3 lst-kix_44ruy757edkv-0 start">
<li class="c5 c6">Manualmente:<br /><img src="http://betabeers.com/uploads/blog/20181005_imagen1.png" alt="" width="550" height="78" /></li>
<li class="c5 c6">Desde un layout: <br /><img src="http://betabeers.com/uploads/blog/20181005_imagen2.png" alt="" width="550" height="52" /></li>
<li class="c5 c6">Desde un ConstraintLayout ya existente:<br /><img src="http://betabeers.com/uploads/blog/20181005_imagen3.png" alt="" width="550" height="66" /></li>
</ul>
<p class="c1"> </p>
<h2 id="h.v15amgodh5r0" class="c15">TransitionManager</h2>
<p class="c5">Esta clase fue a&ntilde;adida en la API 19, su objetivo principal es la ejecuci&oacute;n de animaciones basadas en cambios de escena. En este caso, los cambios de escena se deben a cambios en las constraints de los elementos. En post posteriores veremos m&aacute;s caracter&iacute;sticas de este manager y su gran capacidad.</p>
<h2 id="h.m29rr0wmasxq" class="c15">&iexcl;Empezamos!</h2>
<p class="c5">Una vez que ya tenemos los conceptos te&oacute;ricos claros, vamos a ir paso a paso detallando su implementaci&oacute;n. Es importante se&ntilde;alar que crearemos una vista propia donde realizaremos la animaci&oacute;n, esta vista se llamar&aacute; &ldquo;ExpandableCard.kt&rdquo;.</p>
<p class="c1"> </p>
<p class="c5">La animaci&oacute;n que vamos a realizar es la <a href="https://github.com/franlopjur/ConstraintAnimations/blob/master/imagen4.gif">siguiente</a>.</p>
<p class="c5"> </p>
<h3 id="h.vlmqr0oiofe2" class="c14">1.- Creamos el layout inicial.</h3>
<p class="c5">Este layout tendr&aacute; las constraints o restricciones iniciales para el comienzo de la animaci&oacute;n.</p>
<p class="c5">Para comenzar vamos a definir una serie de guidelines que nos ayudar&aacute;n a delimitar hasta d&oacute;nde se mover&aacute;n nuestras vistas en la animaci&oacute;n. Las guidelines son:</p>
<ul class="c3 lst-kix_u1spdtkqnz2l-0 start">
<li class="c5 c6">card__guideline__left. Tipo porcentaje y vertical, valor 0.1 (10%).</li>
<li class="c5 c6">card__guideline__right. Tipo porcentaje y vertical, valor 0.9 (90%).</li>
<li class="c5 c6">card__guideline__bottom. Tipo porcentaje y horizontal, valor 0.7 (70%).</li>
<li class="c5 c6">card__guideline__top. Tipo porcentaje y horizontal, valor 0.2 (20%).</li>
</ul>
<p class="c1"> </p>
<p class="c5">Las vistas principales con sus constraints son:</p>
<ul class="c3 lst-kix_35f96yh08cab-0 start">
<li class="c5 c6">card__container__image. CardView con la imagen, la cu&aacute;l al inicio debe ocupar toda la vista por eso definiremos sus 4 constraints como &ldquo;parent&rdquo;.</li>
<li class="c5 c6">card__container__info. CardView con el texto de informaci&oacute;n. Al igual que el contenedor de la imagen, ocupar&aacute; tambi&eacute;n todo el ancho y alto ya que estar&aacute; por debajo de la imagen.</li>
<li class="c5 c6">card__label__title. T&iacute;tulo de la vista, estar&aacute; centrado y ocupar&aacute; desde la guideline con id &ldquo;card__guideline__left&rdquo; hasta &ldquo;card__guideline__right&rdquo;.</li>
</ul>
<p class="c1"> </p>
<p class="c5">La imagen inicial que tenemos es:</p>
<p class="c5"><img src="http://betabeers.com/uploads/blog/20181005_imagen5.png" alt="" width="249" height="350" /></p>
<p class="c1"> </p>
<p class="c5"><strong>&iexcl;IMPORTANTE! Todos los hijos (que no nietos) que tenga el ConstraintLayout deben tener un id especificado, ya que el layout que contiene la animaci&oacute;n final debe conocer la posici&oacute;n de todos sus elementos.</strong></p>
<p class="c1"> </p>
<p class="c5">El c&oacute;digo del layout &ldquo;card_collapsed.xml&rdquo; podemos encontrarlo en el siguiente <a class="c9" href="https://www.google.com/url?q=https://github.com/franlopjur/ConstraintAnimations/blob/master/card_collapsed.xml&amp;sa=D&amp;ust=1538696423702000">enlace</a>.</p>
<h3 id="h.h0wtrriemat8" class="c14">2.- Creamos el layout con la animaci&oacute;n final.</h3>
<p class="c5">En este caso NO es necesario especificar atributos referidos a establecer colores, tama&ntilde;o de textos, etc. S&oacute;lo necesitaremos definir las constraints para cada elemento del layout inicial. De esta forma se podr&aacute;n desplazar todas las vistas desde su punto inicial hasta el final.</p>
<p class="c5">En este caso tendremos las siguientes constraints:</p>
<ul class="c3 lst-kix_35f96yh08cab-0">
<li class="c5 c6">card__container__image. Cambiaremos todas sus constraints exceptio la superior, la constraint inicial (&ldquo;layout_constraintStart_toEndOf&rdquo;) la estableceremos al final de la guideline con id &ldquo;card__guideline__left&rdquo;. La final (&ldquo;layout_constraintEnd_toStartOf&rdquo;) pasar&aacute; a ser la guideline con id &ldquo;card__guideline__right&rdquo;. Finalmente, la inferior (&ldquo;layout_constraintBottom_toTopOf&rdquo;) pasar&aacute; a ser la guideline con id &ldquo;card__guideline__bottom&rdquo;.</li>
<li class="c5 c6">card__container__info. Mantendr&aacute; todas sus constraints excepto la superior (&ldquo;layout_constraintTop_toBottomOf&rdquo;) ya que le indicaremos que est&eacute; justo debajo de la guideline con id &ldquo;card__guideline__top&rdquo;.</li>
<li class="c5 c6">card__label__title. Mantiene todas sus constraints.</li>
</ul>
<p class="c1"> </p>
<p class="c5">La imagen final que tenemos gracias a estas constraints es:</p>
<p class="c5"><img src="http://betabeers.com/uploads/blog/20181005_imagen6.png" alt="" width="248" height="400" /></p>
<p class="c1"> </p>
<p class="c5">El c&oacute;digo del layout &ldquo;card_expanded.xml&rdquo; podemos encontrarlo en el siguiente <a class="c9" href="https://www.google.com/url?q=https://github.com/franlopjur/ConstraintAnimations/blob/master/card_expanded.xml&amp;sa=D&amp;ust=1538696423703000">enlace</a>.</p>
<h3 id="h.8l9v3gwwciao" class="c14">3.- Creamos la vista personalizada.</h3>
<p class="c5">Ahora pasamos a implementar nuestra clase &ldquo;ExpandableCard.kt&rdquo; que extender&aacute; de ConstraintLayout. Cada vez que creamos una vista personalizada debemos indicar todos sus constructores, en Kotlin podemos utilizar la anotaci&oacute;n &ldquo;@JvmOverloads&rdquo; que nos ayudar&aacute; a unificar todos los constructores en uno. Despu&eacute;s, simplemente estableceremos el layout inicial a nuestra vista.</p>
<p class="c1"> </p>
<p class="c5">El c&oacute;digo podemos encontrarlo en el siguiente <a class="c9" href="https://www.google.com/url?q=https://github.com/franlopjur/ConstraintAnimations&amp;sa=D&amp;ust=1538696423704000">enlace</a>.</p>
<p class="c5"><img src="http://betabeers.com/uploads/blog/20181005_imagen7.png" alt="" width="750" height="138" /></p>
<h3 id="h.dh25ehsqd8xc" class="c14">4.- Configuramos la animaci&oacute;n.</h3>
<p class="c5">Antes de nada, definimos una variable &ldquo;applyInitAnimation&rdquo; la cual nos ayudar&aacute; a saber el estado (inicial o final) de nuestra animaci&oacute;n.</p>
<p class="c1"> </p>
<p class="c5">Despu&eacute;s, sacamos las constraints de los layouts inicial y finales, de esta forma aplicaremos uno u otro en funci&oacute;n del estado de la misma. Para ello, hacemos uso del m&eacute;todo &ldquo;clone&rdquo; de ConstraintSet, el cu&aacute;l clonar&aacute; las constraints del layout indicado y las establecer&aacute; en las distintas variables:</p>
<ul class="c3 lst-kix_bfncnlk49wb9-0 start">
<li class="c5 c6">Variable &ldquo;constraintsFinal&rdquo; de tipo ConstraintSet. Sacaremos las constraints del layout &ldquo;card_expanded&rdquo;.</li>
<li class="c5 c6">Variable &ldquo;constraintsInitial&rdquo; de tipo ConstraintSet. Sacaremos las constraints del layout &ldquo;card_collapsed&rdquo;.</li>
</ul>
<p class="c1 c16"> </p>
<p class="c5">Una vez que ya las tenemos, aplicamos los distintos ConstraintSet en funci&oacute;n del estado de la misma (controlado por la variable &ldquo;applyInitAnimation&rdquo;) a la vista principal.</p>
<p class="c5">El m&eacute;todo &ldquo;beginDelayedTransition&rdquo; es el encargado de hacer la animaci&oacute;n cuando establezcamos las constraints al layout haciendo uso del m&eacute;todo &ldquo;applyTo&rdquo; de ConstraintSet.</p>
<p class="c1"> </p>
<p class="c5">El c&oacute;digo ser&aacute; el siguiente:</p>
<p class="c5"><img src="http://betabeers.com/uploads/blog/20181005_imagen8.png" alt="" width="700" height="258" /></p>
<p class="c1"> </p>
<p class="c5">Sin m&aacute;s, ya tenemos implementados todos los elementos para nuestra animaci&oacute;n. Podr&eacute;is encontrar todo el c&oacute;digo del proyecto en <a href="https://github.com/franlopjur/ConstraintAnimations">Github</a>. Espero que te haya gustado.</p>
<p class="c1"> </p>
<p class="c1"> </p>]]></content:encoded>
</item><item>
<title><![CDATA[WordPress in jail. Securizar Nuestro Sitio Web]]></title>
<link>https://betabeers.com/blog/wordpress-in-jail-securizar-nuestro-sitio-web-374/</link>
<pubDate>Thu, 27 Sep 2018 21:03:39 +0000</pubDate>
<category>Colaboraciones</category>
<content:encoded><![CDATA[<p><span id="docs-internal-guid-2484bbde-7fff-0bf9-eee0-ba4752327b7d" style="font-size: 11pt; font-family: Arial; color: #000000; background-color: transparent; font-weight: bold; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><img style="border: none; transform: rotate(0rad); display: block; margin-left: auto; margin-right: auto;" src="https://lh4.googleusercontent.com/gzigfS199vgzdsGHUF3HhpH537j0IIIrjVkPJAG207_3NSb5Ie2TOZdyaF3cro2nr5Whd9-IZoJqP8FJvGgtm5_qm76GjcFXAlLYSsSwjqTE5HB3MZSLx-q4MFT3wAt5MpEwymRl" alt="" width="359" height="177" /></span></p>
<p><span id="docs-internal-guid-2484bbde-7fff-0bf9-eee0-ba4752327b7d" style="font-size: 11pt; font-family: Arial; color: #000000; background-color: transparent; font-weight: bold; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span id="docs-internal-guid-60dc5988-7fff-d171-0be6-eed7e84afbb3" style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: bold; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Se ha realizado una estimaci&oacute;n de que tan solo el 45% del tr&aacute;fico de una p&aacute;gina WEB es l&iacute;cito, el resto es debido a bots, herramientas de pirater&iacute;a y spam. Tambi&eacute;n se calcula que uno 30.000 sitios son vulnerados diariamente lo que se traduce en la importancia de implementar nuestro sitio de la manera lo m&aacute;s segura posible. De c&oacute;mo poder realizarlo en WordPress vamos a tratar en este art&iacute;culo.</span></span></p>
<p id="docs-internal-guid-30438996-7fff-08b3-9e7a-b8fcbcd4b2d8" style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Que existan mucho sitios WordPress vulnerados no es debido a la creencia de que esta tecnolog&iacute;a sea insegura, es debida a fallos en la configuraci&oacute;n por parte de los administradores.</span></p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Lo primero que se deber&iacute;a tener en cuenta es elegir bien los complementos a utilizar en nuestro proyecto, ya que de no hacerlo de su sitio oficial (WordPress.org), podr&iacute;amos estar utilizando un plugin vulnerable con c&oacute;digo malicioso, creado por un ciberdelincuente para realizar sus actividades malintencionadas. Esta ser&iacute;a una de las vulnerabilidades m&aacute;s presentes, pero tambi&eacute;n lo son nombres de usuario y contrase&ntilde;as deducibles adem&aacute;s de no mantener nuestro software debidamente actualizado. </span></p>
<p> </p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: bold; font-style: normal; font-variant: normal; text-decoration: underline; -webkit-text-decoration-skip: none; text-decoration-skip-ink: none; vertical-align: baseline; white-space: pre-wrap;">Medidas a seguir para que nuestro sitio WordPress sea m&aacute;s seguro:</span></p>
<p> </p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: bold; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Realiza una copia de seguridad.</span><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> Lo primero que tenemos que tener en cuenta es tener un respaldo, de esta manera tendr&iacute;amos los archivos necesarios para poder replicar nuestro sitio nuevamente. Existen multitud de herramientas para poder realizar nuestra copia de seguridad de forma sencilla, simplemente haciendo una b&uacute;squeda en internet. Hay que tener muy en cuenta no hacer una copia de segurida parcial (solo de la base de datos), esta copia debe de ser completa para evitarnos importantes sustos.</span></p>
<p> </p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: bold; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Tener una contrase&ntilde;a segura.</span><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> Teniendo una contrase&ntilde;a robusta tenemos una gran base para empezar a implementar las dem&aacute;s medidas de seguridad. Hay multitud de recomendaciones para tener una contrase&ntilde;a segura, como son incluir n&uacute;mero y signos especiales, alternar mayusculas con minusculas pero la mas importante de todas es que sea lo m&aacute;s larga posible como por ejemplo una frase pero sin palabras clave (nombre, edad, a&ntilde;os etc). WordPress contiene una herramienta que genera contrase&ntilde;as seguras si se necesita que se puede encontrar dentro del perfil de usuario.</span></p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"> </p>
<p id="docs-internal-guid-ff96ef68-7fff-7fa0-7f69-c8fbb13f9e90" style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: bold; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Nombres de usuario.</span><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> La otra parte de la ecuaci&oacute;n a la hora de autenticarse para acceder a nuestro sitio y as&iacute; evitar que los ciberdelincuentes puedan acceder de manera maliciosa, es elegir bien el nombre de usuario, puesto que se deben de conocer ambos si se quiere acceder al panel de administraci&oacute;n. Cuando se instala WordPress el nombre de usuario por defecto es admin algo que es sobradamente conocido por todo el mundo y que no se suele cambiar por comodidad, pero que estamos ofreciendo a alguien malicioso la mitad del acceso a nuestro sitio. Con lo cual es necesario cambiarlo, para ello se debe de elegir uno siguiendo los mismo criterios que para las contrase&ntilde;as. </span></p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"> </p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: bold; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Controlar el contenido publicado.</span><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> Es muy importante conocer el contenido de lo que publicamos en nuestra p&aacute;gina, es de l&oacute;gica no publicar c&oacute;digo del que no se conf&iacute;e plenamente en la fuente del mismo, puede contener c&oacute;digo malicioso. Si permitimos a otros usuarios publicar en nuestro sitio se debe verificar el contenido de la publicaci&oacute;n. Si se permiten comentarios no validarlos inmediatamente, puesto que pueden contener c&oacute;digo incrustado que podr&iacute;a ser malicioso.</span></p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"> </p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: bold; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Roles de usuario.</span><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> Hay que tener especial cuidado con los permisos que se le dan a los usuarios registrados, para que en caso de registrarse un alguien con malas intenciones no pueda hacer mucho da&ntilde;o. Las opciones son dentro del men&uacute; ajustes generales:</span></p>
<ul>
<li style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr">
<p id="docs-internal-guid-3abad944-7fff-d1e7-112c-3384a95d1eb8" style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">-Suscriptor, tan solo administrar su perfil.</span></p>
</li>
<li style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">-Colaborador, puede escribir publicaciones pero no publicarlas. </span></li>
<li style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"><span id="docs-internal-guid-8050a1d4-7fff-0cdc-2a93-557086f4a136" style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Editor, puede escribir, publicar y administrar cuentas.</span></li>
<li style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"><span id="docs-internal-guid-b70fcb2f-7fff-636f-7ed0-2c9338b07ca2" style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Administrador, acceso completo al sitio.</span></li>
</ul>
<p> </p>
<p id="docs-internal-guid-09d6041c-7fff-44d7-a23e-8b4db9a4635a" style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">De entre todas las posibilidades siempre se debe escoger la del m&iacute;nimo privilegio posible con el que se pueda realizar las funciones que necesita cada usuario.</span></p>
<p> </p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: bold; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Comentarios spam. </span><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Es una pr&aacute;ctica habitual y adem&aacute;s buena pr&aacute;ctica permitir comentarios, puesto que a los buscadores les gusta y nos ser&aacute; de gran utilidad a la hora de elevar puestos en ellos, pero hay que tener especial cuidado porque un ciberdelincuente los puede aprovechar para realizar acciones delictivas. Por lo que no se debe configurar nuestro sitio para que acepte comentarios de forma automatizada, siempre se debe de hacer de forma manual.</span></p>
<p> </p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: bold; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Limitar intentos de logging. </span><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Debe de limitarse la cantidad de veces que puede un usuario introducir usuario y contrase&ntilde;a con la intenci&oacute;n de acceder al sistema, puesto que una pr&aacute;ctica habitual de los delincuentes es realizar ataques de fuerza bruta. Este tipo de ataques tratan de conseguir acceso a base de probar de una forma extremadamente r&aacute;pida multitud de contrase&ntilde;as hasta que dan con la v&aacute;lida, todo realizado con un software programado a tal efecto. Si nosotros limitamos por ejemplo a 3 veces y un determinado tiempo esos intentos de accesos evitaremos esa pr&aacute;ctica. Para realizarlo podemos utilizar los plugins Limit Login Attempts y Login LockDown.</span></p>
<p><br /><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: bold; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Segundo factor de autenticaci&oacute;n. </span><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">El tener un segundo requisito para poder acceder al sistema, es una buena medida de seguridad, puesto que dificultan de manera exponencial </span></p>
<p id="docs-internal-guid-7b4b4d6e-7fff-a540-cb8c-9505d0f8ab25" style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">el que alguien pueda acceder o elevar privilegios sin que deba de tener dicho acceso. Una manera de hacerlo ser&iacute;a que una vez introducimos usuario y contrase&ntilde;a v&aacute;lido, se nos env&iacute;a a nuestro tel&eacute;fono m&oacute;vil un mensaje con un c&oacute;digo el cual tengamos que introducir para autenticarnos. Una forma de poder implementar este sistema en WordPress es con <a href="https://es.wordpress.org/plugins/google-authenticator/">el repositorio de plugins</a> para Google Authenticator que se puede descargar desde la siguiente URL.</span></p>
<p> </p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Proteger pagina de inicio de sesi&oacute;n.</span><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> Esta p&aacute;gina es la puerta de entrada principal a nuestro sitio, con lo cual es l&oacute;gico que le pongamos una protecci&oacute;n extra. Esta protecci&oacute;n se puede realizar implementando multitud de medidas, como pueden ser cambiarle el nombre, introducir un captcha o bloquear ciertas direcciones IP que pudieran intentar comprometerla, como se explica en <a href="https://sensacionweb.com/login-de-wordpress/">el siguiente tutorial</a>.</span></p>
<p> </p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Utilizar m&eacute;todos de cifrado. </span><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">La informaci&oacute;n importante como puede ser toda la relacionada con los usuarios debe de estar debidamente cifrada, para que el caso de que un pirata inform&aacute;tico tuviera acceso al sistema, no pueda leer la informaci&oacute;n en ella contenida. Dependiendo del tipo de base de datos que utilicemos tendr&iacute;a una forma u otra en <a href="https://www.solvetic.com/tutoriales/article/1693-seguridad-de-bases-de-datos-mysql-m&eacute;todos-de-encriptaci&oacute;n/">este tutorial</a> se propone un m&eacute;todo en el caso de que nuestra base de datos sea MySQL. </span></p>
<p> </p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: bold; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Alojamiento WEB. </span><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">No se suele pensar que uno de los mayores peligros de nuestro sitio pueda ser la empresa de hosting que elegimos. Por ejemplo si eligi&eacute;ramos un alojamiento barato puede conllevar a que la tecnolog&iacute;a utilizada en los servidores sea antigua u obsoleta lo cual perjudica seriamente la seguridad. La seguridad en definitiva cuesta dinero si nuestro alojamiento es demasiado barato seguramente sea en detrimento de la misma. A la hora de elegir el hosting debemos de informarnos de las medidas de seguridad y la tecnolog&iacute;a que se utiliza antes de contratar el servicio.</span></p>
<p> </p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: bold; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Permisos en los archivos. </span><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Los archivos en los servidores al igual que los PC contienen un serie de permisos (acceder, leer y escribir), para dar estos permisos debemos seguir el principio de m&iacute;nimo privilegio, para que en caso de que un usuario indebido acceda al sistema no pueda acceder a los archivos y comprometer la informaci&oacute;n. </span></p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"> </p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"> </p>
<p id="docs-internal-guid-87ad993c-7fff-3c58-05ba-21ebdaa9710b" style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: bold; font-style: normal; font-variant: normal; text-decoration: underline; -webkit-text-decoration-skip: none; text-decoration-skip-ink: none; vertical-align: baseline; white-space: pre-wrap;">Configurando la seguridad:</span></p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"> </p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: bold; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Men&uacute; cuentas de usuario:</span></p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"> </p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">En la pesta&ntilde;a nombre de usuario de WP hay que asegurarse de no configurar usuarios con el nombre de admin.</span></p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span id="docs-internal-guid-967acaa6-7fff-d408-dac5-92c06d3bf38d" style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: bold; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Men&uacute; de acceso de usuario:</span></span></p>
<ol>
<li style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: bold; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span id="docs-internal-guid-57b98ac6-7fff-c89b-cb23-945ef21b0580" style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">En la pesta&ntilde;a Bloqueo de inicio de sesi&oacute;n, habilite el Bloqueo de inicio de sesi&oacute;n.</span></span></span></li>
<li style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: bold; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span id="docs-internal-guid-6a25f1cc-7fff-0b77-644c-db52a5e39934" style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">En la pesta&ntilde;a Forzar sesi&oacute;n, active Force WP User Logout.</span></span></span></li>
</ol>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"> </p>
<p id="docs-internal-guid-0b0c1e39-7fff-68ac-48bd-5062d413459b" style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: bold; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Men&uacute; de registro de usuario:</span></p>
<ol>
<li style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: bold; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span id="docs-internal-guid-fff372d9-7fff-ad7d-ba61-257a6f1c24c7" style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> En la pesta&ntilde;a Aprobaci&oacute;n manual, habilite la aprobaci&oacute;n manual de nuevos registros.</span></span></li>
</ol>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"> </p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"><span id="docs-internal-guid-bf521208-7fff-2f6a-17fb-54be763f0db2" style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">2.En la pesta&ntilde;a Captcha de registro, habilite Captcha en la p&aacute;gina de registro.</span></p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"><span id="docs-internal-guid-5d17cbfb-7fff-88af-05c3-a40c22ba5432" style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: bold; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Seguridad de la base de datos:</span></p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"> </p>
<p id="docs-internal-guid-56134432-7fff-c413-2c78-de869a44317a" style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; margin-left: 36pt; text-align: justify;" dir="ltr"><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">1.En la pesta&ntilde;a Prefijo de DB, aseg&uacute;rese de no estar usando el wp_ predeterminado como prefijo de su tabla.</span></p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; margin-left: 36pt; text-align: justify;" dir="ltr"> </p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-indent: 36pt; text-align: justify;" dir="ltr"><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">2.En la pesta&ntilde;a Copia de respaldo de BD, habilite copias de seguridad autom&aacute;ticas.</span></p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-indent: 36pt; text-align: justify;" dir="ltr"> </p>
<p id="docs-internal-guid-9abb10db-7fff-22cf-195c-25a9095798cf" style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: bold; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Seguridad del sistema de archivos:</span></p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-indent: 36pt; text-align: justify;" dir="ltr"> </p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-indent: 36pt; text-align: justify;" dir="ltr"><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">1.En la pesta&ntilde;a Permisos de archivos realice las acciones recomendadas. </span></p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-indent: 36pt; text-align: justify;" dir="ltr"> </p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; margin-left: 36pt; text-align: justify;" dir="ltr"><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">2.En la pesta&ntilde;a Edici&oacute;n de archivos PHP, deshabilite la capacidad de editar archivos PHP.</span></p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-indent: 36pt; text-align: justify;" dir="ltr"> </p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; margin-left: 36pt; text-align: justify;" dir="ltr"><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">3.En la pesta&ntilde;a Acceso a archivos WP, evite el acceso a los archivos de instalaci&oacute;n predeterminados de WP.</span></p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; margin-left: 36pt; text-align: justify;" dir="ltr"> </p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: bold; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Firewall:</span></p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; margin-left: 36pt; text-align: justify;" dir="ltr"> </p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-indent: 36pt; text-align: justify;" dir="ltr"><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">1.En la pesta&ntilde;a Reglas b&aacute;sicas del firewall, habilite la protecci&oacute;n b&aacute;sica del firewall.</span></p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; margin-left: 36pt; text-align: justify;" dir="ltr"> </p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-indent: 36pt; text-align: justify;" dir="ltr"><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">2.Habilite el acceso de bloque al archivo debug.log.</span></p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; margin-left: 36pt; text-align: justify;" dir="ltr"> </p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; margin-left: 36pt; text-align: justify;" dir="ltr"><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">3.En la pesta&ntilde;a Reglas adicionales del firewall, habilitar Deshabilitar vistas de &iacute;ndice.</span></p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; margin-left: 36pt; text-align: justify;" dir="ltr"> </p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-indent: 36pt; text-align: justify;" dir="ltr"><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">4.Habilite el Deshabilitar rastreo y seguimiento.</span></p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; margin-left: 36pt; text-align: justify;" dir="ltr"> </p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-indent: 36pt; text-align: justify;" dir="ltr"><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">5.Habilitar la publicaci&oacute;n de comentarios de proxies prohibidos.</span></p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; margin-left: 36pt; text-align: justify;" dir="ltr"> </p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-indent: 36pt; text-align: justify;" dir="ltr"><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">6.Habilite el Habilitar filtro de cadena de caracteres avanzado.</span></p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; margin-left: 36pt; text-align: justify;" dir="ltr"> </p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; margin-left: 36pt; text-align: justify;" dir="ltr"><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">7.En la pesta&ntilde;a 6G Blackwall Firewall Rules, marque la opci&oacute;n Habilitar protecci&oacute;n Firewall 6G.</span></p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; margin-left: 36pt; text-align: justify;" dir="ltr"> </p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-indent: 36pt; text-align: justify;" dir="ltr"><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">8.En la pesta&ntilde;a Bots de Internet, habilite la opci&oacute;n Bloquear Googlebots falsos.</span></p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; margin-left: 36pt; text-align: justify;" dir="ltr"> </p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; margin-left: 36pt; text-align: justify;" dir="ltr"><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">9.En la pesta&ntilde;a Evitar enlaces activos, habilite la opci&oacute;n Prevenir interconexi&oacute;n de im&aacute;genes.</span></p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; margin-left: 36pt; text-align: justify;" dir="ltr"> </p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; margin-left: 36pt; text-align: justify;" dir="ltr"><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">10.En la pesta&ntilde;a Detecci&oacute;n 404, marque la opci&oacute;n Habilitar Detecci&oacute;n y Bloqueo de IP 404.</span></p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; margin-left: 36pt; text-align: justify;" dir="ltr"> </p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: bold; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Protecci&oacute;n Contra fuerza bruta:</span></p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; margin-left: 36pt; text-align: justify;" dir="ltr"> </p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; margin-left: 36pt; text-align: justify;" dir="ltr"><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">1.En la Prevenci&oacute;n de fuerza bruta basada en cookies, realice la prueba de cookies para asegurarse de que su sitio pueda usar este m&eacute;todo de protecci&oacute;n. Si puede, ingrese una Palabra secreta y luego active Prevenci&oacute;n de ataque de fuerza bruta en esta pesta&ntilde;a. Si no puede, vaya a la p&aacute;gina Cambiar nombre de inicio de sesi&oacute;n.</span></p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; margin-left: 36pt; text-align: justify;" dir="ltr"> </p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-indent: 36pt; text-align: justify;" dir="ltr"><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">2.En la pesta&ntilde;a Login Captcha, habilite cualquiera de los captchas que quiera usar.</span></p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; margin-left: 36pt; text-align: justify;" dir="ltr"> </p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; margin-left: 36pt; text-align: justify;" dir="ltr"><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">3.En la pesta&ntilde;a Honeypot, marque la opci&oacute;n Habilitar Honeypot en la p&aacute;gina de inicio de sesi&oacute;n.</span></p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; margin-left: 36pt; text-align: justify;" dir="ltr"> </p>
<p id="docs-internal-guid-388dd59f-7fff-93ac-1f18-1a2d6f61b3bd" style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: bold; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Prevenci&oacute;n contra SPAM:</span></p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"> </p>
<p id="docs-internal-guid-7f2a4cc7-7fff-e0fe-0222-26f8cf336c0a" style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; margin-left: 36pt; text-align: justify;" dir="ltr"><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: bold; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">1.</span><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">En la pesta&ntilde;a Comment Spam, marque la opci&oacute;n para habilitar Captcha On Comment Forms. Marque la opci&oacute;n Bloquear Spambots para no publicar comentarios.</span></p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"> </p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; margin-left: 36pt; text-align: justify;" dir="ltr"><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">2.En la pesta&ntilde;a de Supervisi&oacute;n de IP de SPAM de correo electr&oacute;nico, marque la opci&oacute;n para habilitar Bloqueo autom&aacute;tico de IP de comentario de SPAM. Te recomiendo que ingreses un n&uacute;mero bajo en el cuadro N&uacute;mero m&iacute;nimo de comentarios SPAM.</span></p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"> </p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: bold; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Conclusiones.</span></p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"> </p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Actualmente una gran cantidad de sitios est&aacute;n creados con WordPress, eso puede ser malo desde el punto de vista de seguridad, puesto que los piratas inform&aacute;ticos estar&aacute;n m&aacute;s interesados en romperla, pero como en las dem&aacute;s tecnolog&iacute;as si cuidamos su configuraci&oacute;n conseguiremos pon&eacute;rselo mucho m&aacute;s dif&iacute;cil, ya que como se suele decir</span><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: bold; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> la seguridad absoluta no existe pero si unos valores de la misma bastante razonables</span><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">, espero que estas recomendaciones os sean de gran utilidad.</span></p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: bold; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /><br /><br /></span></p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: bold; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Referencia.</span></p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"> </p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">WordPress Security 2017 for Webmasters. Dr Andy Williams. https://www.amazon.it/Wordpress-Beginners-2017-Step-Step-ebook/dp/B01M7YERUP</span></p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"><span style="font-size: 11pt; font-family: Ubuntu; color: #000000; background-color: transparent; font-weight: bold; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /><br /></span></p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; margin-left: 36pt; text-align: justify;" dir="ltr"> </p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; margin-left: 36pt; text-align: justify;" dir="ltr"> </p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-indent: 36pt; text-align: justify;" dir="ltr"> </p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-indent: 36pt; text-align: justify;" dir="ltr"> </p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"> </p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"> </p>
<p> </p>
<p style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt; text-align: justify;" dir="ltr"> </p>
<p> </p>]]></content:encoded>
</item><item>
<title><![CDATA[Desarrollo de temas WordPress con Gulp]]></title>
<link>https://betabeers.com/blog/desarrollo-temas-wordpress-gulp-373/</link>
<pubDate>Fri, 21 Sep 2018 07:57:45 +0000</pubDate>
<category>Colaboraciones</category>
<content:encoded><![CDATA[<p>Gracias a la documentaci&oacute;n exhaustiva que nos encontramos en el <a href="https://codex.wordpress.org/es:Main_Page" rel="noopener" target="_blank">Codex de WordPress</a> podemos disponer en cualquier momento de gran informaci&oacute;n acerca de las posibilidades en el desarrollo de temas para WordPress.</p>
<p>En efecto, gracias a la documentaci&oacute;n, foros de soporte, y recursos formativos, no necesitamos m&aacute;s que un editor de c&oacute;digo y una selecci&oacute;n adecuada de elementos gr&aacute;ficos para poner a punto nuestro proyecto creado con WordPress y scorrespondiente tema de dise&ntilde;o y funcionalidades b&aacute;sicas.</p>
<p><img style="display: block; margin-left: auto; margin-right: auto;" src="http://betabeers.com/uploads/blog/20180921_desarrollo-temas-wordpress-gulp-betabeers-blog-imagen.jpg" alt="Desarrollo temas WordPress Gulp" width="600" height="400" /></p>
<p>De todos modos, en el mundo del desarrollo frontend nos encontramos con posibilidades importantes de automatizaci&oacute;n para el despliegue de acciones durante el proceso de desarrollo que nos permitir&aacute;n agilizar el trabajo ahorando tiempo en diferentes tareas, y evitar realizar trabajos repetitivos y que aportan poco valor al propio desarrollo.</p>
<p>Una de esas herramientas es Gulp, y es la que centrar&aacute; el proceso que vamos a explicar en el siguiente art&iacute;culo.</p>
<h3>&iquest;Qu&eacute; es Gulp?</h3>
<p>Gulp es un sistema constru&iacute;do en JavaScript que permite tomar archivos de origen con instrucciones y transformarlos en versiones optimizadas a nuestro antojo, todo ello en cuesti&oacute;n de segundos.</p>
<p>No no extenderemos aqu&iacute; en el proceso de instalaci&oacute;n de <a href="https://gulpjs.com/" rel="noopener" target="_blank">Gulp.js</a> y nos remitiremos a la documentaci&oacute;n oficial de la herramienta donde podremos conocer todos los detalles.</p>
<p>Tan solo apuntar que para que podamos ejecutar Gulp.js, necesitaremos previamente haber completado la instalaci&oacute;n de Node.js si no lo hubi&eacute;ramos hecho antes en nuestro equipo.</p>
<p>En el caso que nos ocupa podremos automatizar gracias a Gulp algunas tareas como las siguientes:</p>
<ul>
<li>Crear y actualizar nuevos ficheros de plantilla PHP para nuestro tema</li>
<li>Optimizar las im&aacute;genes del dise&ntilde;o</li>
<li>Compilar ficheros SCSS mediante preprocesadores en un nuevo fichero CSS</li>
<li>Compilar ficheros JavaScript depur&aacute;ndolos y minific&aacute;ndolos</li>
<li>Actualizar el navegador en el que estamos trabajando para visualizar los cambios en tiempo real</li>
</ul>
<h3>Configurando el entorno de desarrollo</h3>
<p>Necesitaremos disponer de los ficheros de origen para nuestro tema que nos permitir&aacute;n disponer de los fichero e im&aacute;genes sin modificar, para despu&eacute;s ser entregados en una suerte de construcci&oacute;n final con los archivos definitivos de nuestro tema.</p>
<p>En este caso, nos resultar&aacute; interesante trabajar con dos directorios diferenciados. Uno para los ficheros de origen y configuraci&oacute;n, y otro para el resultado final, con el fin de que podamos desplegar los cambios realizados utilizando como fuente solamente ese directorio final de archivos definitivos.</p>
<p>Todo ello podr&iacute;a estar englobado dentro de un directorio /themefiles donde podremos a&ntilde;adir toda la informaci&oacute;n de origen y un directorio /themebuild donde podremos a&ntilde;adir los archivos resultantes del proceso.</p>
<p>Una estructura de directorios interesante para los archivos de origen que podemos utilizar es la siguiente:</p>
<ul>
<li>template - para los ficheros PHP originales del tema</li>
<li>images - para los archivos de imagen</li>
<li>scss - para los archivos SCSS originales</li>
<li>js - para los ficheros JS necesarios</li>
</ul>
<p>Entre esta misma informaci&oacute;n de origen como tambi&eacute;n pueden ser las dependencias de Gulp que necesitaremos instalar para arrancar los correspondientes procesos.</p>
<h3>Instalando dependencias</h3>
<p>Para instalar las dependencias necesarias podemos navegar hasta el directorio /themefiles y ejecutar la siguiente concatenaci&oacute;n de &oacute;rdenes desde la terminal.</p>
<pre>npm install --save-dev autoprefixer browser-sync css-mqpacker cssnano gulp gulp-concat gulp-deporder gulp-imagemin gulp-newer gulp-postcss gulp-sass gulp-strip-debug gulp-uglify gulp-util postcss-assets

</pre>
<p><a href="https://gist.github.com/fernandiez/0cf59bb0065dcf2caa15981fd4d77f7a" rel="noopener" target="_blank">Enlace a Gist</a></p>
<p>A continuaci&oacute;n veremos con m&aacute;s detalle cada una de las dependencias y su objetivo.</p>
<h3>Configuraci&oacute;n de Gulp</h3>
<p>Uno de los pasos m&aacute;s importantes del proceso de preparaci&oacute;n es la configuraci&oacute;n de Gulp. Tendremos que crear dentro del directorio de origen un archivo gulpfile.js con las variables deseadas.</p>
<p>Este puede ser un buen ejemplo para arrancar:</p>
<pre>// Configurar Gulp.js
'use strict';

const

// directorios origen y finales
dir = {
src : 'src/',
build : '/themebuild/'
},

// Gulp plugins
gulp = require('gulp'),
gutil = require('gulp-util'),
newer = require('gulp-newer'),
imagemin = require('gulp-imagemin'),
sass = require('gulp-sass'),
postcss = require('gulp-postcss'),
deporder = require('gulp-deporder'),
concat = require('gulp-concat'),
stripdebug = require('gulp-strip-debug'),
uglify = require('gulp-uglify')
;

// Browsersync
var browsersync = false;

// PHP ajustes
const php = {
src : dir.src + 'template/**/*.php',
build : dir.build
};

// archivos PHP
gulp.task('php', () =&gt; {
return gulp.src(php.src)
.pipe(newer(php.build))
.pipe(gulp.dest(php.build));
});

</pre>
<p><a href="https://gist.github.com/fernandiez/2404260bda1135c989914fd0be2596e5" rel="noopener" target="_blank">Enlace a Gist</a></p>
<p>En este archivo de configuraci&oacute;n estamos definiendo las carpetas por defecto, los m&oacute;dulos que deben cargarse y una tarea de actualizaci&oacute;n de los ficheros PHP del directorio final de construcci&oacute;n del theme.</p>
<p>Si creamos nuevos archivos PHP en el directorio de origen y hacemos correr el siguiente comando, veremos c&oacute;mo los ficheros de origen se copian el directorio final.</p>
<pre>gulp php
</pre>
<h3>Optimizaci&oacute;n de im&aacute;genes</h3>
<p>A&ntilde;adiendo las siguientes &oacute;rdenes en nuestro fichero gistfile.js podremos a&ntilde;adir un trabajo de optimizaci&oacute;n de im&aacute;genes.</p>
<pre>// imagen ajustes
const images = {
src : dir.src + 'images/**/*',
build : dir.build + 'images/'
};

// imagen procesado
gulp.task('images', () =&gt; {
return gulp.src(images.src)
.pipe(newer(images.build))
.pipe(imagemin())
.pipe(gulp.dest(images.build));
});

</pre>
<p><a href="https://gist.github.com/fernandiez/9aef9468727b437f1e23f71fb8a5d489" rel="noopener" target="_blank">Enlace a Gist</a></p>
<p>La orden que debemos utilizar desde la terminal para ejecutar este proceso es la siguiente, actualizando as&iacute; las im&aacute;gene en el directorio de construcci&oacute;n final:</p>
<pre>gulp images

</pre>
<h3>Compilar SCSS</h3>
<p>Si trabajamos con SCSS, y concretamente con Sass para procesar nuestras hojas de estilos, podremos automatizar esta tarea, y no depender m&aacute;s de cualquier otro software. En efecto, no podemos procesar directamente este tipo de preprocesadores directamente desde WordPress, motivo por el cual, tendremos que preparar con antelaci&oacute;n al fichero style.css para su inclusi&oacute;n entre los archivos finales.</p>
<pre>// CSS ajustes
var css = {
src : dir.src + 'scss/style.scss',
watch : dir.src + 'scss/**/*',
build : dir.build,
sassOpts: {
outputStyle : 'nested',
imagePath : images.build,
precision : 3,
errLogToConsole : true
},
processors: [
require('postcss-assets')({
loadPaths: ['images/'],
basePath: dir.build,
baseUrl: '/wp-content/themes/wptheme/'
}),
require('autoprefixer')({
browsers: ['last 2 versions', '&gt; 2%']
}),
require('css-mqpacker'),
require('cssnano')
]
};

// CSS procesado
gulp.task('css', ['images'], () =&gt; {
return gulp.src(css.src)
.pipe(sass(css.sassOpts))
.pipe(postcss(css.processors))
.pipe(gulp.dest(css.build))
.pipe(browsersync ? browsersync.reload({ stream: true }) : gutil.noop());
});

</pre>
<p><a href="https://gist.github.com/fernandiez/ab5c22c10a920bf8f4275d1186edfe29" rel="noopener" target="_blank">Enlace a Gist</a></p>
<p>Con esta tarea automatizada de Gulp, podremos gestionar directamente en una sola orden las siguientes tareas automatizadas:</p>
<ul>
<li>Preparar las im&aacute;genes en primer lugar</li>
<li>Compilar el c&oacute;digo Sass en el archivo de origen</li>
<li>Generar el archivo style.css en el directorio final</li>
<li>Preparar la ejecuci&oacute;n del refresco autom&aacute;tico en LiveReload</li>
</ul>
<p>Para ello tendremos simplemente que ejecutar la siguiente orden desde la terminal:</p>
<pre>gulp css

</pre>
<h3>Compilaci&oacute;n de JavaScript</h3>
<p>Siguiendo los mismos pasos del proceso anterior podremos lanzar tareas automatizadas para procesar los ficheros JS que necesitemos en el tema WordPress con el que estamos trabajando.</p>
<p>Conseguiremos con el siguiente ejemplo procesar todos los archivos JS inclu&iacute;dos, concatenar los archivos en uno solo y minificar el resultado export&aacute;ndolo en el directorio de construcci&oacute;n final.</p>
<p>Las l&iacute;neas que deberemos a&ntilde;adir al archivo de configuraci&oacute;n son las siguientes:</p>
<pre>// JavaScript ajustes
const js = {
src : dir.src + 'js/**/*',
build : dir.build + 'js/',
filename : 'scripts.js'
};

// JavaScript procesado
gulp.task('js', () =&gt; {

return gulp.src(js.src)
.pipe(deporder())
.pipe(concat(js.filename))
.pipe(stripdebug())
.pipe(uglify())
.pipe(gulp.dest(js.build))
.pipe(browsersync ? browsersync.reload({ stream: true }) : gutil.noop());

});

</pre>
<p><a href="https://gist.github.com/fernandiez/3ca0a300cf0a4ceaadd61daed94652aa" rel="noopener" target="_blank">Enlace a Gist</a></p>
<p>Para poder lanzar las tareas tendremos que ejecutar el siguiente comando:</p>
<pre>gulp js

</pre>
<p>Si queremos agilizar los procesos anteriormente mencionados y hacer correr cada una de las secciones, podemos definir un nuevo comando que incluya todas las &oacute;rdenes.</p>
<p>Debemos a&ntilde;adir estas &oacute;rdenes al final del fichero gulpfile.js</p>
<pre>// recopilar acciones
gulp.task('build', ['php', 'css', 'js']);

</pre>
<p><a href="https://gist.github.com/fernandiez/1eacd3568029adcedd574f9633101df2" rel="noopener" target="_blank">Enlace a Gist</a></p>
<p>De este modo podremos a&ntilde;adir un solo comando para hacer correr las tareas relacionadas con ficheros CSS, JS e im&aacute;genes.</p>
<pre>gulp build

</pre>
<h3>Visualizaci&oacute;n y refresco autom&aacute;tico</h3>
<p>Uno de los pasos finales y posiblemente m&aacute;s significativos de todo el proceso, es poder observar los resultados del trabajo directamente en el navegador.</p>
<p>En este primer proceso de configuraci&oacute;n podremos lanzar la visualizaci&oacute;n de los cambios en el navegador cuando realmente se hayan producido, sin tener que salir del editor de c&oacute;digo, hacer click en el navegador o accionar el teclado.</p>
<pre>// Browsersync ajustes
const syncOpts = {
proxy : 'localhost',
files : dir.build + '**/*',
open : false,
notify : false,
ghostMode : false,
ui: {
port: 8001
}
};

// browser-sync
gulp.task('browsersync', () =&gt; {
if (browsersync === false) {
browsersync = require('browser-sync').create();
browsersync.init(syncOpts);
}
});

</pre>
<p><a href="https://gist.github.com/fernandiez/58a1910283524d5bbce9fee60ee4ae93" rel="noopener" target="_blank">Enlace a Gist</a></p>
<p>Ahora podemos a&ntilde;adir una tarea de observaci&oacute;n o vigilancia para lanzar Browserwync cuando sea necesario, es decir, cuando se hayan observado cambios en alguno de los ficheros.</p>
<p>Esta informaci&oacute;n deber&aacute; incluirse en el archivo de configuraci&oacute;n gulpfile.js en el que venimos trabajando desde el inicio del art&iacute;culo.</p>
<pre>// vigilar archivo
gulp.task('watch', ['browsersync'], () =&gt; {

// cambios en archivos
gulp.watch(php.src, ['php'], browsersync ? browsersync.reload : {});

// cambios en imagen
gulp.watch(images.src, ['images']);

// cambios CSS
gulp.watch(css.watch, ['css']);

// cambios JavaScript 
gulp.watch(js.src, ['js']);

});

</pre>
<p><a href="https://gist.github.com/fernandiez/15336964a9e2f5fd485ac4b88a7d9f0b" rel="noopener" target="_blank">Enlace a Gist</a></p>
<p>Finalmente podemos a&ntilde;adir una tarea completa que nos complete las acciones anteriores con la observaci&oacute;n de los cambios realizados de manera constante para ejecutar nuevas tareas.</p>
<pre>// tareas Gulp por defecto
gulp.task('default', ['build', 'watch']);

</pre>
<p><a href="https://gist.github.com/fernandiez/bd4c085bbdc27e5a5a59f9d18eebbe6f" rel="noopener" target="_blank">Enlace a Gist</a></p>
<p>Simplemente deberemos ejecutar la tarea gulp en la terminal para accionar el resultado final del proceso que nos permita concatenar las tareas automatizadas, m&aacute;s la vigilancia mientras haya cambios y el lanzamiento en el navegador del resultado final.</p>
<pre>gulp

</pre>
<h3>Conclusiones</h3>
<p>Hemos tratado de dar algunos ejemplos interesantes para poder iniciarnos en las tareas de automatizaci&oacute;n de tareas en el desarrollo de temas para WordPress.</p>
<p>Es importante en este caso destacar que no s&oacute;lo nos podemos quedar aqu&iacute;, existen numerosos repositorios que nos permitir&aacute;n introducir nuevas tareas o crear acciones m&aacute;s complejas si las necesitamos.</p>
<p>Adem&aacute;s, a medida que nos vayamos familiarizando con esta metodolog&iacute;a podremos ir creando nuestros propios archivos de configuraci&oacute;n de automatizaciones y personalizar al completo el flujo de trabajo.</p>]]></content:encoded>
</item><item>
<title><![CDATA[Mejora tu código con preprocesadores CSS. SASS.]]></title>
<link>https://betabeers.com/blog/mejora-tu-codigo-preprocesadores-css-sass-372/</link>
<pubDate>Fri, 29 Jun 2018 10:35:21 +0000</pubDate>
<category>Colaboraciones</category>
<content:encoded><![CDATA[<p><em><img style="display: block; margin-left: auto; margin-right: auto;" src="http://betabeers.com/uploads/blog/20180629_imageCssPost.jpg" alt="" width="400" height="174" /></em></p>
<p><em>Este art&iacute;culo ha sido escrito por <a href="https://twitter.com/jrodgut">Javier Rodr&iacute;guez</a> y publicado originalmente en el blog de <a href="https://solidgeargroup.com/preprocesadores-css-sass?lang=es">Solid GEAR</a>.</em></p>
<p style="text-align: justify;">Cuando realizamos una p&aacute;gina web o <a href="https://solidgeargroup.com/desarrollo-de-apps-hibridas-con-ionic?lang=es">una aplicaci&oacute;n h&iacute;brida</a>, una de las primeras cosas que notamos es que nuestro c&oacute;digo CSS suele complicarse y desorganizarse con facilidad a medida que el dise&ntilde;o se complica. CSS, sobre todo en sus primeras versiones, carece de herramientas propias para facilitar la organizaci&oacute;n y reutilizaci&oacute;n de propiedades. Es ah&iacute; donde surgen los preprocesadores CSS para ayudarnos con esa tarea. En esta entrada en concreto, hablaremos de Sass y mostraremos algunas de sus principales funcionalidades.</p>
<h2>&iquest;Qu&eacute; es un preprocesador CSS?</h2>
<p style="text-align: justify;">Un preprocesador CSS es simplemente una herramienta que nos permitir&aacute; escribir nuestros estilos en un lenguaje espec&iacute;fico y que luego, tras un proceso de compilaci&oacute;n, generar&aacute; unos ficheros CSS con los estilos equivalentes escritos en CSS. Este lenguaje inicial, el cual ser&aacute; diferente seg&uacute;n el preprocesador elegido, suele disponer de &uacute;tiles para facilitar la reutilizaci&oacute;n y modularizaci&oacute;n del c&oacute;digo.</p>
<p style="text-align: justify;">As&iacute;, el uso de un preprocesador es transparente a la p&aacute;gina, ya que simplemente escribiremos nuestro c&oacute;digo usando el lenguaje del procesador, pero luego en nuestro c&oacute;digo HTML enlazaremos la hoja de estilos como siempre.</p>
<p style="text-align: justify;">Existen multitud de procesadores diferentes, aunque probablemente los m&aacute;s conocidos sean Sass y <a href="http://lesscss.org/">Less</a>. Ambos disponen de funcionalidades parecidas y, aunque en esta entrada hablaremos de Sass, Less (y probablemente muchos otros preprocesadores) son tambi&eacute;n una buena opci&oacute;n.</p>
<h2>Sass</h2>
<p style="text-align: justify;">Sass es un preprocesador CSS escrito originalmente en Ruby, aunque actualmente existen otras implementaciones disponibles, como por ejemplo en JavaScript, como se puede ver en su <a href="https://sass-lang.com/documentation/file.SASS_REFERENCE.html">p&aacute;gina oficial</a>. Existen dos sintaxis disponibles. Nosotros nos centraremos en la sintaxis SCSS (Sassy CSS) que emplearemos en ficheros .scss.</p>
<p style="text-align: justify;">SCSS es un lenguaje montado sobre el est&aacute;ndar CSS. Eso significa que un fichero SCSS que contenga s&oacute;lo CSS compilar&iacute;a sin problemas. Esto facilita enormentente la transici&oacute;n de aplicaciones existentes en CSS a SCSS, ya que inicialmente simplemente tendr&iacute;amos que compilar los ficheros SCSS con el mismo contenido que los CSSs y usar el CSS generado como hasta entonces.</p>
<h3>&iquest;C&oacute;mo instalar Sass?</h3>
<p style="text-align: justify;">En nuestro caso instalaremos la distribuci&oacute;n de JavaScript ya que solemos disponer de <a href="https://nodejs.org/">Node</a> instalado en nuestros equipos y aprovecharemos <a href="https://www.npmjs.com/">npm</a> para ello. Simplemente este comando instalar&aacute; la &uacute;ltima versi&oacute;n disponible.</p>
<p><code>npm install -g sass</code></p>
<p style="text-align: justify;">Para compilar todos nuestros ficheros .scss a CSS podremos usar el siguiente comando en el que pondremos la ruta hasta la carpeta en la que est&aacute;n los SCSS y la ruta en la que queremos generar la misma estructura pero en ficheros CSS.</p>
<p><code>sass ruta/hasta/carpeta/scss:ruta/hasta/carpeta/css</code></p>
<p>Veamos ahora algunas de las principales ventajas que obtendremos con Sass.</p>
<h3>Mejor organizaci&oacute;</h3>
<p>Sass permite la anidaci&oacute;n de unos selectores dentro de otros. Por ejemplo, si en CSS tenemos unos estilos para el selector <code>nav</code> y otros para <code>nav .active</code> podemos meter <code>.active</code> entro del bloque de <code>nav</code> (a la vez que por ejemplo a&ntilde;adimos estilos a <code>nav</code>). Queda m&aacute;s claro viendo el siguiente ejemplo en el que podemos ver c&oacute;mo ser&iacute;a el fichero CSS tras compilar ese SCSS.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="css" data-enlighter-group="nested_tags" data-enlighter-title="SCSS">nav {<br /> font-size: 2em;</pre>
<p> </p>
<p>.active {<br /> font-weight: bold;<br /> }<br />}</p>
<pre class="EnlighterJSRAW" data-enlighter-language="css" data-enlighter-group="nested_tags" data-enlighter-title="CSS generado">nav {<br /> font-size: 2em;<br />}<br />nav .active {<br /> font-weight: bold;<br />}</pre>
<p> </p>
<p style="text-align: justify;">Existe otra forma de anidar nuestros estilos tambi&eacute;n cuando se usa en el selector <code>&gt;</code> para buscar un elemento directamente bajo otro. Por ejemplo para el selector <code>nav &gt; .active</code> lo pondr&iacute;amos de esta manera:</p>
<p> </p>
<pre class="EnlighterJSRAW" data-enlighter-language="css" data-enlighter-group="d_nested_tags" data-enlighter-title="SCSS">nav {<br /> font-size: 2em;<br /> &gt; active {<br /> font-weight: bold;<br /> }<br />}</pre>
<pre class="EnlighterJSRAW" data-enlighter-language="css" data-enlighter-group="d_nested_tags" data-enlighter-title="CSS generado">nav {<br /> font-size: 2em;<br />}<br />nav &gt; active {<br /> font-weight: bold;<br />}</pre>
<p> </p>
<p style="text-align: justify;">Tambi&eacute;n podemos anidar el uso de un selector y del mismo aplicando alg&uacute;n modificador adicional. Por ejemplo podr&iacute;amos anidar<code> a:hover</code> dentro de <code>a</code>, ya que <code>&amp;</code> tiene el significado del selector del bloque actual.</p>
<p> </p>
<pre class="EnlighterJSRAW" data-enlighter-language="css" data-enlighter-group="nested_ampersand" data-enlighter-title="SCSS">a {<br /> cursor: pointer;<br /> &amp;:hover {<br /> color: blue;<br /> }<br />}</pre>
<pre class="EnlighterJSRAW" data-enlighter-language="css" data-enlighter-group="nested_ampersand" data-enlighter-title="CSS generado">a {<br /> cursor: pointer;<br />}<br />a:hover {<br /> color: blue;<br />}</pre>
<p>Un caso particular en cuanto a c&oacute;mo anidar nuestros selectores es el de las media queries. En este caso, en lugar de envolver con @media al selector al que aplica, se introduce dentro de &eacute;l. Este cambio hace que sea m&aacute;s legible ver c&oacute;mo cambian las propiedades en funci&oacute;n de las restricciones indicadas en la media query.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="css" data-enlighter-group="media_nested" data-enlighter-title="SCSS">.card {<br /> width: 50%;<br /> @media only screen and (max-width: 768px) {<br /> width: 100%;<br /> }<br />}</pre>
<pre class="EnlighterJSRAW" data-enlighter-language="css" data-enlighter-group="media_nested" data-enlighter-title="CSS generado">.card {<br /> width: 50%;<br />}<br />@media only screen and (max-width: 768px) {<br /> .card {<br /> width: 100%;<br /> }<br />}</pre>
<p style="text-align: justify;">Por &uacute;ltimo, tambi&eacute;n podemos agrupar propiedades de la misma familia (como ocurre por ejemplo con margin, padding, font...) en el que todas tienen una ra&iacute;z com&uacute;n. En este caso al anidar, para indicar que es una propiedad, usaremos <code>:</code> al final del prefijo com&uacute;n.</p>
<p>p {</p>
<pre class="EnlighterJSRAW" data-enlighter-language="css" data-enlighter-group="nested_prop" data-enlighter-title="SCSS"> font: {<br /> size: 2em;<br /> weight: bold;<br /> }<br />}</pre>
<pre class="EnlighterJSRAW" data-enlighter-language="css" data-enlighter-group="nested_prop" data-enlighter-title="CSS generado">p {<br /> font-size: 2em;<br /> font-weight: bold;<br />}</pre>
<p style="text-align: justify;">Haciendo uso de todos estos tipos de anidaci&oacute;n reforzamos que el equipo agrupe los selectores y propiedades haciendo un c&oacute;digo m&aacute;s legible y m&aacute;s organizado.</p>
<p> </p>
<p> </p>
<h3>Reutilizaci&oacute;n de c&oacute;digo</h3>
<p style="text-align: justify;">Seguramente la funcionalidades que m&aacute;s nos ayudar&aacute;n para mejorar la mantenibilidad de nuestro c&oacute;digo son aquellas que nos permitan la reutilizaci&oacute;n de partes del mismo. Sass dispone de diferentes elementos para ello comos por ejemplo variables, mixins o la herencia.</p>
<h4>Variables</h4>
<p style="text-align: justify;">Al igual que en otros lenguajes una variable simplemente almacenar&aacute; un valor que podremos usar en otras partes de nuestro SCSS. La variable puede almacenar diferentes tipos de valores, que se corresponden con los que usar&iacute;amos en CSS: n&uacute;meros, colores, dimensiones... La definici&oacute;n de una variable comienza con <code>$</code> y su uso es tan sencillo como en el siguiente ejemplo.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="css" data-enlighter-group="scss_variables" data-enlighter-title="SCSS">$primary-color:black;<br />$base-font-size: 1em;</pre>
<p>body {<br /> background-color: $primary-color;<br /> font-size: $base-font-size;<br />}</p>
<p>.card {<br /> color: $primary-color;<br /> background-color: invert($primary-color);<br /> font-size: $base-font-size * 1.2;<br />}</p>
<pre class="EnlighterJSRAW" data-enlighter-language="css" data-enlighter-group="scss_variables" data-enlighter-title="CSS generado">body {<br /> background-color: black;<br /> font-size: 1em;<br />}</pre>
<p>.card {<br /> color: black;<br /> background-color: white;<br /> font-size: 1.2em;<br />}</p>
<p style="text-align: justify;">Si nos fijamos en el ejemplo hemos definido un par de variables para definir un tama&ntilde;o de fuente y color base para nuestra web y a partir de ellos vamos generando los estilos. De esta forma si modificamos por ejemplo el color base, este se actualiza acordemente en todos los estilos asociados. En el ejemplo se puede ver tambi&eacute;n como podemos hacer uso de operadores matem&aacute;ticos e incluso de funciones (en ese caso <code>invert</code> que calcula el color opuesto). Si nos hace falta podremos incluso implementar nuestras propias funciones.</p>
<p style="text-align: justify;">Es importante tener en cuenta que, aunque CSS <a href="https://www.w3schools.com/css/css3_variables.asp">ya dispone de variables,</a> e incluso puede <a href="https://www.w3schools.com/cssref/func_calc.asp">realizar c&aacute;lculos sencillos</a>, al hacer uso de ellos necesitamos que el navegador empleado tenga una versi&oacute;n en la que ya se soporte dichas funcionalidades. Este problema no ocurre en SCSS ya que la compilaci&oacute;n se realiza antes de desplegar nuestra web, por lo que el CSS generado ya no incluir&aacute; las variables o funciones, y por ello ser&aacute; transparente al navegador.</p>
<h4>Mixins</h4>
<p style="text-align: justify;">Un mixin es simplemente un conjunto de propiedades que podremos aplicar en diferentes lugares de nuestro SCSS. Con ellos podremos centralizar nuestra l&oacute;gica y que nuestro evitar c&oacute;digo duplicado en distintos selectores. Adem&aacute;s, los mixins admiten par&aacute;metros de entrada, por lo que podremos hacer uso de ellos para crear mixins gen&eacute;ricos y personalizar cada estilo cuando hagamos uso de ellos.</p>
<p style="text-align: justify;">El mixin simplemente se define con la palabra clave <code>@mixin</code> tras la cual definiremos el nombre del mixin y sus variables de entrada si es que tiene. A la hora de usarlo, emplearemos <code>@include</code> con los par&aacute;metros a usar, como se hace en el siguiente ejemplo.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="css" data-enlighter-group="mixins" data-enlighter-title="SCSS">@mixin custom-bar ($background-color, $font-color, $bar-height){<br /> background-color: $background-color;<br /> color: $font-color;<br /> height: $bar-height;<br /> width: 100%;<br />}</pre>
<p>nav {<br /> @include custom-bar(black, white, 2em);<br /> margin-bottom: 1em;<br />}</p>
<p>footer {<br /> @include custom-bar(black, lightgray, 1.5em);<br /> font-size: 0.8em;<br />}</p>
<pre class="EnlighterJSRAW" data-enlighter-language="css" data-enlighter-group="mixins" data-enlighter-title="CSS generado">nav {<br /> background-color: black;<br /> color: white;<br /> height: 2em;<br /> width: 100%;<br /> margin-bottom: 1em;<br />}</pre>
<p>footer {<br /> background-color: black;<br /> color: lightgray;<br /> height: 1.5em;<br /> width: 100%;<br /> font-size: 0.8em;<br />}</p>
<h4>Herencia</h4>
<p style="text-align: justify;">Existe otra forma similar a los mixins para aplicar grupo de propiedades a diferentes selectores, que es la herencia. En la herencia simplemente definiremos las propiedades a heredar dentro de un bloque precedido con <code>%</code>, y a la hora de usarlo emplearemos la palabra clave <code>@extend</code>.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="css" data-enlighter-group="inheritance" data-enlighter-title="SCSS">%custom-bar {<br /> background-color: black;<br /> color: white;<br /> height: 2em;<br /> width: 100%;<br />}</pre>
<p>nav {<br /> @extend %custom-bar;<br /> margin-bottom: 1em;<br />}</p>
<p>footer {<br /> @extend %custom-bar;<br /> font-size: 0.8em;<br />}</p>
<pre class="EnlighterJSRAW" data-enlighter-language="css" data-enlighter-group="inheritance" data-enlighter-title="CSS generado">footer, nav {<br /> background-color: black;<br /> color: white;<br /> height: 2em;<br /> width: 100%;<br />}</pre>
<p>nav {<br /> margin-bottom: 1em;<br />}</p>
<p>footer {<br /> font-size: 0.8em;<br />}<br /><br /></p>
<p style="text-align: justify;">Si comparamos la salida generada con un mixin a la generada con el uso de la herencia, podemos ver c&oacute;mo el c&oacute;digo generado con la herencia parece m&aacute;s compacto y menos repetido. Sin embargo, la herencia tiene la limitaci&oacute;n de que no admite variables de entrada.</p>
<h3>Modularizaci&oacute;n</h3>
<p style="text-align: justify;">A medida que se complica nuestro dise&ntilde;o podremos dividir nuestros estilos en diferentes ficheros y cargarlos en otros ficheros si as&iacute; lo necesitamos. Simplemente poniendo <code>@import 'ruta-hasta-el-fichero.css'</code>, cargaremos toda la informaci&oacute;n de ese SCSS y ser&iacute;a lo mismo que si tuvi&eacute;ramos el contenido de ese fichero pegado en ese punto.</p>
<p style="text-align: justify;">Eso nos ayuda no s&oacute;lo para una mejor organizaci&oacute;n modularizando nuestros estilos, sino que tambi&eacute;n podremos reusar un mismo m&oacute;dulo en diferentes ficheros. Un caso t&iacute;pico es tener un fichero con las configuraci&oacute;n base de nuestra web (con los colores, tama&ntilde;os...) y cargarlos en todos los ficheros para aplicar los estilos en base a ello.</p>
<p style="text-align: justify;">Este es el caso del popular framework de frontend <a href="https://getbootstrap.com/">Bootstrap</a>. Este producto est&aacute; escrito en SASS y dispone de un fichero SCSS de variables, con el que configura diferentes aspectos claves de la visualizaci&oacute;n de cada uno de los elementos. Gracias a ello, podremos cargar el fichero principal de Bootstrap en nuestro SCSS (tras habernos descargado <a href="https://github.com/twbs/bootstrap">los fuentes</a> SCSS de bootstrap) y hacer una sobrecarga de alguna de las propiedades que queramos antes de cargar el fichero SCSS principal. As&iacute; dispondremos de un Bootstrap completamente personalizado.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="css">// Sobreescribir variables de Bootrap para personalizarlo<br />$body-bg: lightgray;<br />$body-color: brown;<br />$font-family-base: "Times New Roman", Times, serif;<br />$headings-line-height: 1.8;</pre>
<p>// Importar Bootstrap<br />@import "path-to-bootstrap-folder/scss/bootstrap";</p>
<h2>Conclusiones</h2>
<p style="text-align: justify;">Hemos visto como el uso de un preprocesador CSS nos ayuda a organizar mejor nuestro c&oacute;digo CSS. Al estar menos duplicado, cumpliremos mejor <a href="https://es.wikipedia.org/wiki/No_te_repitas">el principio DRY (Don't Repeat Yourself)</a> y seremos m&aacute;s r&aacute;pidos tanto al escribirlo por primera vez c&oacute;mo al realizar futuras modificaciones. Aunque en esta entrada nos hemos centrado en Sass, podemos obtener resultados similares con otras herramientas parecidas, as&iacute; que os animamos a emplear esta u otra para disfrutar de todas sus ventajas.</p>]]></content:encoded>
</item><item>
<title><![CDATA[Code Security Check: Verificación de seguridad en aplicaciones]]></title>
<link>https://betabeers.com/blog/code-security-check-verificacion-seguridad-aplicaciones-371/</link>
<pubDate>Fri, 22 Jun 2018 09:57:03 +0000</pubDate>
<category>Colaboraciones</category>
<content:encoded><![CDATA[<p><img style="display: block; margin-left: auto; margin-right: auto;" src="https://lh5.googleusercontent.com/sE_inOHDr67joHTE_Cc1Sa5NQ7MErPIMMY9kHEBjGJnVZLnG9hiyMiCSyA0ozGa2Cgi6OT-AQBEgyJd43T87wHvrODgPXvRjutXkgEPwsq5rGOL-nNPzD2nYVSMCvHMdqgaULhWb" alt="" width="602" height="215" /></p>
<p> </p>
<p dir="ltr"><strong>En el presente art&iacute;culo vamos a tratar el est&aacute;ndar de verificaci&oacute;n de seguridad en aplicaciones (ASVS de sus siglas en ingl&eacute;s) creado por OWASP. Este est&aacute;ndar re&uacute;ne unos controles de seguridad funcionales necesarios para dise&ntilde;ar, desarrollar y testear aplicaciones WEB.</strong></p>
<p> </p>
<p dir="ltr">ASVS tiene como objetivo ayudar a los desarrolladores a crear aplicaciones m&aacute;s seguras y a su mantenimiento y crear un consenso entre proveedores de productos de seguridad y consumidores.</p>
<p> </p>
<p dir="ltr"><strong>Define tres niveles de verificaci&oacute;n de seguridad:</strong></p>
<p> </p>
<p dir="ltr">-Nivel 1 dirigido a todo tipo de software.</p>
<p dir="ltr">-Nivel 2 para aplicaciones que manejan datos sensibles.</p>
<p dir="ltr">-Nivel 3 a utilizar en aplicaciones cr&iacute;ticas como las que manejan datos m&eacute;dicos o transacciones bancarias.</p>
<p> </p>
<p dir="ltr"><img src="https://lh6.googleusercontent.com/udKfpzsLwWeWM-o9W6_p6VAbjuPscpEDmPQkiPncp8f4_KyzNISrVbdGnjftzR928H7S9IaitI2SyI1E2qBnK70RtBfJ4nrlYI-ks4vPHXwKzqcZTp2kP9Mtvp3QzWPhID5dqEnN" alt="" width="602" height="281" /></p>
<p dir="ltr">Dependiendo de la industria o el sector al que se vaya a dedicar la aplicaci&oacute;n se debe de aplicar un nivel u otro como por ejemplo, una WEB de venta al por menor contiene una amenaza espec&iacute;fica puesto que tiene informaci&oacute;n de pagos ya que realizar&iacute;a transacciones financieras por lo tanto habr&iacute;a que aplicar el nivel 1 puesto que es aplicable a todas las aplicaciones, nivel 2 reservado a aplicaciones con cantidades peque&ntilde;as o moderadas de funcionalidad de datos y nivel 3 por contener sistema de pagos de punto de venta.</p>
<p> </p>
<p dir="ltr"><strong>Los controles que tendr&iacute;amos que realizar a nuestras aplicaciones ser&iacute;an:</strong></p>
<p dir="ltr"><strong> 1. Arquitectura y dise&ntilde;o:</strong></p>
<ol>
<li dir="ltr">
<p dir="ltr">Comprobar que todos los componentes de la aplicaci&oacute;n se definen en funci&oacute;n de las funciones de negocio.</p>
</li>
<li dir="ltr">
<p dir="ltr">Comprobar que todos los controles de seguridad tienen una implementaci&oacute;n centralizada.</p>
</li>
<li dir="ltr">
<p dir="ltr">Comprobar que los componentes est&aacute;n separados unos de otros mediante controles de seguridad.</p>
</li>
<li dir="ltr">
<p dir="ltr">Comprobar que todos los componentes de la aplicaci&oacute;n est&aacute;n libres de vulnerabilidades conocidas.</p>
</li>
</ol>
<p dir="ltr"><strong> 2. Verificaci&oacute;n de autenticaci&oacute;n:</strong></p>
<ol start="5">
<li dir="ltr">
<p dir="ltr">Comprobar que todas las p&aacute;ginas y recursos requieren autenticaci&oacute;n menos las que deban de estar p&uacute;blicas.</p>
</li>
<li dir="ltr">
<p dir="ltr">Comprobar que los campos de credenciales no reflejen las contrase&ntilde;as de usuario.</p>
</li>
<li dir="ltr">
<p dir="ltr">Comprobar que los campos de contrase&ntilde;a fomentan utilizar frases como autenticaci&oacute;n.</p>
</li>
<li dir="ltr">
<p dir="ltr">Comprobar que la funcionalidad de cambio de contrase&ntilde;a requiere de la contrase&ntilde;a anterior y contiene un campo de confirmaci&oacute;n.</p>
</li>
<li dir="ltr">
<p dir="ltr">Comprobar que las contrase&ntilde;as almacenadas se a utilizado un mecanismo de hashing esto es que no est&aacute;n almacenadas en texto plano.</p>
</li>
<li dir="ltr">
<p dir="ltr">Comprobar que las credenciales viajan cifradas.</p>
</li>
<li dir="ltr">
<p dir="ltr">Comprobar que no se utilizan contrase&ntilde;as por defectos en la aplicaci&oacute;n.</p>
</li>
<li dir="ltr">
<p dir="ltr">Comprobar que existen medidas para bloquear el uso de contrase&ntilde;as d&eacute;biles.</p>
</li>
<li dir="ltr">
<p dir="ltr">Comprobar que se utiliza doble factor de autenticaci&oacute;n.</p>
</li>
<li dir="ltr">
<p dir="ltr">Comprobar que las interfaces de administraci&oacute;n no son accesibles a cualquier usuario.</p>
</li>
</ol>
<p dir="ltr"> </p>
<p dir="ltr"> </p>
<p dir="ltr"> </p>
<p dir="ltr"><strong>3. Verificaci&oacute;n de gesti&oacute;n de sesiones:</strong></p>
<ol>
<li dir="ltr">
<p dir="ltr">Comprobar que las sesiones se invalidan cuando el usuario cierra la sesi&oacute;n.</p>
</li>
<li dir="ltr">
<p dir="ltr">Comprobar que se invalidan las sesiones despu&eacute;s de un periodo de inactividad.</p>
</li>
<li dir="ltr">
<p dir="ltr">Comprobar que el identificador de sesi&oacute;n no se muestra en la URL.</p>
</li>
<li dir="ltr">
<p dir="ltr">Comprobar que la aplicaci&oacute;n limita el n&uacute;mero de sesiones concurrentes.</p>
</li>
</ol>
<p dir="ltr"><strong>4. Verificaci&oacute;n del control de acceso:</strong></p>
<ol>
<li dir="ltr">
<p dir="ltr">Comprobar que existe el principio de m&iacute;nimo privilegio.</p>
</li>
<li dir="ltr">
<p dir="ltr">Comprobar que el acceso a registros sensibles est&aacute; protegido.</p>
</li>
<li dir="ltr">
<p dir="ltr">Comprobar que los atributos de usuario no pueden ser manipulados por usuarios finales.</p>
</li>
<li dir="ltr">
<p dir="ltr">Comprobar que las acciones de control de acceso son registradas.</p>
</li>
</ol>
<p dir="ltr"><strong>5. Verificaci&oacute;n para manejo de entrada de datos maliciosa:</strong></p>
<ol>
<li dir="ltr">
<p dir="ltr">Comprobar que el entorno de ejecuci&oacute;n no es susceptible a desbordamiento de buffer.</p>
</li>
<li dir="ltr">
<p dir="ltr">Comprobar que se aplican rutinas de entradas del lado del servidor.</p>
</li>
<li dir="ltr">
<p dir="ltr">Comprobar que todas as consultas a bases de datos est&aacute;n parametrizadas y no son susceptibles de SQL injection.</p>
</li>
<li dir="ltr">
<p dir="ltr">Comprobar que la aplicaci&oacute;n no es susceptible de inyecci&oacute;n LDAP.</p>
</li>
<li dir="ltr">
<p dir="ltr">Comprobar que la aplicaci&oacute;n contenga defensas contra ataques de contaminaci&oacute;n de par&aacute;metros HTTP.</p>
</li>
</ol>
<p dir="ltr"><strong>6. Verificaci&oacute;n para la criptograf&iacute;a en el almacenamiento:</strong></p>
<ol>
<li dir="ltr">
<p dir="ltr">Comprobar que el generador de n&uacute;meros aleatorios es fiable.</p>
</li>
<li dir="ltr">
<p dir="ltr">Comprobar que los m&oacute;dulos criptogr&aacute;ficos operen seg&uacute;n pol&iacute;ticas de seguridad publicadas.</p>
</li>
<li dir="ltr">
<p dir="ltr">Comprobar que existe una pol&iacute;tica expl&iacute;cita para el manejo de claves criptogr&aacute;ficas.</p>
</li>
</ol>
<p dir="ltr"><strong>7. Verificaci&oacute;n de gesti&oacute;n y registro de errores.</strong></p>
<ol>
<li dir="ltr">
<p dir="ltr">Comprobar que no se registra informaci&oacute;n confidencial si no es necesaria.</p>
</li>
<li dir="ltr">
<p dir="ltr">Comprobar que toda la informaci&oacute;n registrada se gestiona de forma segura.</p>
</li>
<li dir="ltr">
<p dir="ltr">Comprobar que los registros de bit&aacute;coras poseen un ciclo de vida &uacute;til lo m&aacute;s corta posible.</p>
</li>
<li dir="ltr">
<p dir="ltr">Comprobar que los registros de seguridad no puedan ser manipulados con acceso no autorizado.</p>
</li>
</ol>
<p dir="ltr"><strong>8. Verificaci&oacute;n de protecci&oacute;n de datos:</strong></p>
<ol>
<li dir="ltr">
<p dir="ltr">Comprobar que los formularios de datos sensibles tengan desactivada la funci&oacute;n de autocompletar.</p>
</li>
<li dir="ltr">
<p dir="ltr">Comprobar que en la aplicaci&oacute;n existe un mecanismo de borrado de datos sensibles transcurrido el tiempo definido por la pol&iacute;tica de retenci&oacute;n.</p>
</li>
<li dir="ltr">
<p dir="ltr">Comprobar que los datos almacenados en el cliente no contengan informaci&oacute;n personal identificable.</p>
</li>
</ol>
<p dir="ltr"><strong>9. Verificaci&oacute;n de seguridad en las comunicaciones:</strong></p>
<ol>
<li dir="ltr">
<p dir="ltr">Comprobar que puede construirse la cadena de confianza desde una Autoridad de Certificaci&oacute;n para un certificado TLS del servidor.</p>
</li>
<li dir="ltr">
<p dir="ltr">Comprobar que se utiliza TLS para todas las comunicaciones.</p>
</li>
<li dir="ltr">
<p dir="ltr">Comprobar que existe una &uacute;nica implementaci&oacute;n est&aacute;ndar de TLS.</p>
</li>
<li dir="ltr">
<p dir="ltr">Comprobar que se utilizan &uacute;nicamente algoritmos y protocolos de seguridad en comunicaciones fuertes.</p>
</li>
<li dir="ltr">
<p dir="ltr">Comprobar que la configuraci&oacute;n de TLS est&aacute; en l&iacute;nea con las mejores pr&aacute;cticas actuales.</p>
</li>
</ol>
<p dir="ltr"><strong>10. Verificaci&oacute;n de configuraci&oacute;n de seguridad HTTP:</strong></p>
<ol>
<li dir="ltr">
<p dir="ltr">Comprobar que el servidor de aplicaciones est&aacute; convenientemente endurecido en la configuraci&oacute;n preestablecida.</p>
</li>
<li dir="ltr">
<p dir="ltr">Comprobar que en toda respuesta HTTP contiene un conjunto de caracteres seguros.</p>
</li>
<li dir="ltr">
<p dir="ltr">Comprobar que la aplicaci&oacute;n tan solo acepta m&eacute;todos de solicitud HTTP necesarios como GET y POST.</p>
</li>
</ol>
<p dir="ltr"><strong>11. Verificaci&oacute;n para la l&oacute;gica de negocio:</strong></p>
<ol>
<li dir="ltr">
<p dir="ltr">Comprobar que el flujo de la l&oacute;gica de negocio es secuencial y en orden.</p>
</li>
<li dir="ltr">
<p dir="ltr">Comprobar que la l&oacute;gica de negocio incluye l&iacute;mites para detectar y evitar ataques automatizados.</p>
</li>
</ol>
<p dir="ltr"><strong>Conclusi&oacute;n.</strong></p>
<p dir="ltr">No solo se hace necesario a la hora de crear nuestras aplicaciones tener especial cuidado hablando desde el punto de vista de la seguridad de la misma, en sus fases de dise&ntilde;o y codificaci&oacute;n, sino que tambi&eacute;n es muy importante tener un m&eacute;todo de testeo de la misma met&oacute;dico y fuerte, para minimizar en la manera de lo posible una vulnerabilidad que pueda ser utilizada por un cibercriminal, ya que la seguridad total no existe, al menos debemos de testear nuestra aplicaciones con el fin de que se produzcan el menor n&uacute;mero de fallos desde el punto de vista de la seguridad.</p>
<p> </p>
<p dir="ltr">Piensa mal y acertar&aacute;s codificando de una manera m&aacute;s segura, aunque la seguridad completa no existe.</p>
<p> </p>
<p dir="ltr"><strong>Referencia.</strong></p>
<p dir="ltr">Est&aacute;ndar de Verificaci&oacute;n de Seguridad en aplicaciones 3.0.1</p>
<p dir="ltr">Versi&oacute;n Abril de 2017</p>]]></content:encoded>
</item><item>
<title><![CDATA[DataBinding (2) - Eventos, Includes y Observables]]></title>
<link>https://betabeers.com/blog/databinding-2-eventos-includes-observables-370/</link>
<pubDate>Fri, 15 Jun 2018 08:32:33 +0000</pubDate>
<category>Colaboraciones</category>
<content:encoded><![CDATA[ <br>
<p class="c17"><img style="display: block; margin-left: auto; margin-right: auto;" src="http://betabeers.com/uploads/blog/20180615_image_1.png" alt="" width="450" height="264" /></p>
<p class="c1 c5"> </p>
<p class="c1"><span>En el </span><span class="c10"><a class="c14" href="https://www.google.com/url?q=https://betabeers.com/blog/databinding-cada-vez-menos-codigo-nuestras-clases-androidmeetskotlin-366/&amp;sa=D&amp;ust=1529018633429000">anterior post</a></span><span class="c2"> ya vimos c&oacute;mo pasar nuestro modelo al XML con el fin de eliminar la l&oacute;gica de pintado / seteo de variables en las clases. En esta ocasi&oacute;n vamos a profundizar un poco m&aacute;s y veremos las dem&aacute;s ventajas y potencial que nos ofrece esta librer&iacute;a. Sin m&aacute;s, !vamos a ello!</span></p>
<p class="c1 c5"> </p>
<h2 id="h.k1fovy1azn5z" class="c13"><span class="c8">Vinculando eventos de UI</span></h2>
<p class="c1"><span class="c2">Tenemos dos formas de manejar eventos:</span></p>
<h3 id="h.vsxbpzuaqzy6" class="c6"><span class="c11">Referencias a m&eacute;todos</span></h3>
<p class="c1"><span class="c9">Los eventos pueden vincularse directamente con los m&eacute;todos del manejador o controlador que los tenga, similar a la forma en que </span><strong><span class="c9 c7">android:onClick</span></strong><span class="c9"> asigna un m&eacute;todo en una actividad pero aportando una ventaja:</span><span class="c9 c7"> </span><span class="c7">los fallos se ver&aacute;n en tiempo de compilaci&oacute;n</span><span>. Si definimos interfaces que no coinciden, el fallo lo veremos al compilar y no posteriormente (no funcione el click, crashes, etc).</span><span class="c4"> Los pasos que tenemos que seguir son:</span></p>
<ul class="c3 lst-kix_ro6mpnsjfujw-0 start">
<li class="c0"><span class="c4">Definimos nuestro m&eacute;todo en la propia Activity o en una clase Handler aparte. Dicha definici&oacute;n ser&aacute; b&aacute;sica, por lo que s&oacute;lo recibir&aacute; un par&aacute;metro de tipo View (ya que haremos el onClick).</span></li>
</ul>
<p class="c1 c5"> </p>
<p class="c1"><img style="display: block; margin-left: auto; margin-right: auto;" src="http://betabeers.com/uploads/blog/20180615_image_2.png" alt="" width="400" height="77" /></p>
<p class="c1 c5"> </p>
<ul class="c3 lst-kix_ro6mpnsjfujw-0">
<li class="c0"><span class="c4">Definimos la variable en el XML.</span></li>
</ul>
<p class="c1 c5"> </p>
<p class="c1"><img style="display: block; margin-left: auto; margin-right: auto;" src="http://betabeers.com/uploads/blog/20180615_image_3.png" alt="" width="600" height="68" /></p>
<p class="c1 c5"> </p>
<ul class="c3 lst-kix_ro6mpnsjfujw-0">
<li class="c0"><span class="c9">Establecemos el onClick siguiendo la siguiente nomenclatura: </span><strong><span class="c9 c7">android:onClick=&rdquo;@{</span><span class="c7 c12">NombreVariable::nombreMetodo}&rdquo;</span></strong></li>
</ul>
<p class="c1 c5"> </p>
<p class="c1"><img style="display: block; margin-left: auto; margin-right: auto;" src="http://betabeers.com/uploads/blog/20180615_image_4.png" alt="" width="650" height="33" /></p>
<p class="c1 c5"> </p>
<ul class="c3 lst-kix_ro6mpnsjfujw-0">
<li class="c0"><span class="c9">Establecemos valor en la variable &ldquo;</span><span class="c9 c7">detailActivityHandler</span><span class="c4">&rdquo;.</span></li>
</ul>
<p class="c1 c5"> </p>
<p class="c1"><img style="display: block; margin-left: auto; margin-right: auto;" src="http://betabeers.com/uploads/blog/20180615_image_5.png" alt="" width="400" height="42" /></p>
<p class="c1 c5"> </p>
<h3 id="h.mze9cw9vuhmv" class="c6"><span class="c11">Vinculando receptores</span></h3>
<p class="c1"><span>Mediante el uso de </span><span class="c7">lambdas </span><span class="c2">podemos establecer m&eacute;todos que reciban par&aacute;metros m&aacute;s complejos y posteriormente capturar los eventos desde el XML. Los pasos a seguir son:</span></p>
<ul class="c3 lst-kix_sajzob482xl4-0 start">
<li class="c0"><span class="c2">Definimos el m&eacute;todo.</span></li>
</ul>
<p class="c1 c5"> </p>
<p class="c1"><img style="display: block; margin-left: auto; margin-right: auto;" src="http://betabeers.com/uploads/blog/20180615_image_6.png" alt="" width="650" height="90" /></p>
<p class="c1 c5"> </p>
<ul class="c3 lst-kix_sajzob482xl4-0">
<li class="c0"><span class="c2">Definimos la variable en XML (como en el caso anterior de &ldquo;Referencias a m&eacute;todos&rdquo;).</span></li>
<li class="c0"><span class="c2">Hacemos uso de la expresi&oacute;n lambda para enviar el evento desde XML.</span></li>
</ul>
<p class="c1 c5"> </p>
<p class="c1"><img style="display: block; margin-left: auto; margin-right: auto;" src="http://betabeers.com/uploads/blog/20180615_image_7.png" alt="" width="700" height="27" /></p>
<p class="c1 c5"> </p>
<ul class="c3 lst-kix_sajzob482xl4-0">
<li class="c0"><span class="c9">Establecemos valor en la variable &ldquo;</span><span class="c9 c7">detailActivityHandler</span><span class="c9">&rdquo;. (Igual que en</span><span> &ldquo;Referencias a m&eacute;todos&rdquo;</span><span class="c4"> ).</span></li>
</ul>
<p class="c1 c5"> </p>
<p class="c1 c5"> </p>
<p class="c1"><span class="c2">Ya sea usando uno u otro, DataBinding proporciona un nexo de enlace entre nuestros eventos de UI y los m&eacute;todos en las clases por lo que es una fuerte herramienta para ahorrar c&oacute;digo de las vistas.</span></p>
<h2 id="h.va1nme7rn7o" class="c13"><span class="c8">!Cuidado con lo que hacemos!</span></h2>
<p class="c1"><span class="c2">Sin embargo, esta forma de referenciar m&eacute;todos debe usarse con cabeza y no incluir m&eacute;todos muy complejos o con muchos par&aacute;metros ya que conseguiremos empeorar la lectura y comprensi&oacute;n de nuestro c&oacute;digo de forma muy notable.</span></p>
<h2 id="h.oksbqsy2f8l3" class="c13"><span class="c8">Comprobaciones con los nulos</span></h2>
<p class="c1"><span>Si queremos establecer un valor u otro en funci&oacute;n de si nuestro objeto es nulo o no, podemos usar el operador <strong>&ldquo;</strong></span><strong>??&rdquo;</strong></p>
<p class="c1 c5"><img style="display: block; margin-left: auto; margin-right: auto;" src="http://betabeers.com/uploads/blog/20180615_image_8.png" alt="" width="700" height="30" /></p>
<p class="c1"><span class="c2">Si &ldquo;androidVersion.description&rdquo; es null pintar&aacute; lo que tenga definido en strings.xml en &ldquo;detail__empty_description&rdquo;.</span></p>
<p class="c1 c5"> </p>
<p class="c1"><span class="c2">Esta expresi&oacute;n es equivalente a:</span></p>
<p class="c1 c5"> </p>
<p class="c1"><img style="display: block; margin-left: auto; margin-right: auto;" src="http://betabeers.com/uploads/blog/20180615_image_9.png" alt="" width="700" height="59" /></p>
<h2 id="h.z47u7vx4or4c" class="c13"><span class="c8">Includes</span></h2>
<p class="c1"><span>Para poder pasar variables desde un XML a otro que tiene la etiqueta <strong>&lt;include&gt;</strong> es necesario hacer uso de la propiedad</span><strong><span class="c7"> &ldquo;bind:nombreDeLaVariable=@{nombreDeLaVariable}&rdquo;</span></strong><span class="c2"> y adem&aacute;s, tienen que tener el mismo nombre en ambos XML.</span></p>
<p class="c1"><span class="c2">Por ejemplo, tenemos la variable definida como &ldquo;androidVersion&rdquo; en nuestro XML llamado &ldquo;activity_detail.xml&rdquo; y en nuestro XML &ldquo;view_description.xml&rdquo; de tal forma:</span></p>
<p class="c1 c5"> </p>
<p class="c1"><img style="display: block; margin-left: auto; margin-right: auto;" src="http://betabeers.com/uploads/blog/20180615_image_10.png" alt="" width="600" height="90" /></p>
<p class="c1 c5"> </p>
<p class="c1"><span class="c2">Adem&aacute;s, tenemos en &ldquo;activity_detail.xml&rdquo; tambi&eacute;n una etiqueta &lt;include&gt; incluyendo el layout &ldquo;include_description.xml&rdquo; que contiene un layout con un TextView donde pintaremos una propiedad de dicha variable.</span></p>
<p class="c1 c5"> </p>
<p class="c1"><span class="c2">Para que nos funcione, debemos hacer los siguientes pasos:</span></p>
<ul class="c3 lst-kix_un316cbk0zw1-0 start">
<li class="c0"><span class="c2">Dar un id a nuestra etiqueta include.</span></li>
<li class="c0"><span>Establecer una propiedad llamada </span><strong><span class="c12 c7">bind:androidVersion=@{androidVersion}</span></strong></li>
<li class="c0"><span class="c2"> Ya podemos establecer en el TextView la propiedad que necesitemos de nuestra variable.</span></li>
</ul>
<p class="c1 c5"> </p>
<p class="c1"><span class="c2">De tal forma nuestro include quedar&iacute;a:</span></p>
<p class="c1"><img style="display: block; margin-left: auto; margin-right: auto;" src="http://betabeers.com/uploads/blog/20180615_image_11.png" alt="" width="500" height="72" /></p>
<p class="c1 c5"> </p>
<p class="c1"><span class="c2">En cuanto a nuestro c&oacute;digo en DetailActivity.kt</span></p>
<p class="c1"><img style="display: block; margin-left: auto; margin-right: auto;" src="http://betabeers.com/uploads/blog/20180615_image_12.png" alt="" width="700" height="75" /></p>
<p class="c1 c5"> </p>
<p class="c1"><span class="c2">Y en nuestro XML include_description.xml:</span></p>
<p class="c1"><img style="display: block; margin-left: auto; margin-right: auto;" src="http://betabeers.com/uploads/blog/20180615_image_13.png" alt="" width="500" height="236" /></p>
<h2 id="h.xclkuclv61e8" class="c13"><span class="c8">Ejemplo con cambios de modelo (Observable)</span></h2>
<p class="c1"><span>Como ya comentamos en el </span><span class="c10"><a class="c14" href="https://www.google.com/url?q=https://betabeers.com/blog/databinding-cada-vez-menos-codigo-nuestras-clases-androidmeetskotlin-366/&amp;sa=D&amp;ust=1529018633440000">post anterior</a></span><span class="c2">, existe la posibilidad de vincular nuestro modelo a nuestra vista con la opci&oacute;n de que se actualice la UI autom&aacute;ticamente ante cambios en el mismo. Los pasos que hay que seguir para implementarlo son:</span></p>
<p class="c1 c5"> </p>
<p class="c1"><strong><span class="c12 c7">Importante: Nuestro c&oacute;digo XML no sufrir&aacute; ning&uacute;n cambio si nuestro modelo es Observable o no.</span></strong></p>
<p class="c1 c5"> </p>
<ul class="c3 lst-kix_f58bljqej4xv-0 start">
<li class="c0"><span class="c2">Nuestro modelo tiene que <strong>extender de Observable</strong>.</span></li>
</ul>
<p class="c1 c5"> </p>
<p class="c1"><img style="display: block; margin-left: auto; margin-right: auto;" src="http://betabeers.com/uploads/blog/20180615_image_14.png" alt="" width="700" height="44" /></p>
<p class="c1 c5"> </p>
<ul class="c3 lst-kix_f58bljqej4xv-0">
<li class="c0"><span>Para establecer los par&aacute;metros que tendr&aacute;n la posibilidad de actualizarse autom&aacute;ticamente, los marcaremos con la anotaci&oacute;n </span><strong><span class="c7">@Bindable</span></strong><span class="c2">. De esta forma estar&aacute;n disponibles en nuestra clase autogenerada BR.</span></li>
<li class="c0"><span>Para los par&aacute;metros que tengan dicha anotaci&oacute;n, estableceremos un set y despu&eacute;s de establecer el valor, llamaremos al m&eacute;todo </span><strong><span class="c7">notifyPropertyChanged()</span></strong><span class="c2"> y le pasaremos el id de la clase BR. De esta forma informaremos a la UI para que se actualice autom&aacute;ticamente sin que tengamos que ponerlo en nuestra Activity o Fragment repetitivamente.</span></li>
</ul>
<p class="c1 c5"> </p>
<p class="c1"><img style="display: block; margin-left: auto; margin-right: auto;" src="http://betabeers.com/uploads/blog/20180615_image_15.png" alt="" width="450" height="167" /></p>
<p class="c1 c5"> </p>
<ul class="c3 lst-kix_f58bljqej4xv-0">
<li class="c0"><span class="c2">Ahora simplemente estableceremos el valor de nuestro modelo y la UI se actualizar&aacute; autom&aacute;ticamente.</span></li>
</ul>
<p class="c1 c5"> </p>
<p class="c1"><img style="display: block; margin-left: auto; margin-right: auto;" src="http://betabeers.com/uploads/blog/20180615_image_16.png" alt="" width="750" height="64" /></p>
<p class="c1 c5"> </p>
<p class="c1"><span>Para el caso de que tengamos una lista observable simplemente debemos establecer el tipo a </span><strong><span>ObservableArrayList&lt;AndroidVersionModel&gt;</span></strong><span> y a&ntilde;adir o eliminar elementos a la misma.</span></p>
<h2 id="h.tauu4at7wbbx" class="c13"><span data-mce-mark="1">No lo tiene todo&hellip;</span><span class="c8" data-mce-mark="1"> :,(</span></h2>
<p class="c1"><span class="c2">Existen una serie de propiedades que no podemos usar en nuestro XML con DataBinding, estas propiedades son:</span></p>
<ul class="c3 lst-kix_ujrgrb5vrab6-0 start">
<li class="c0"><span class="c2">this</span></li>
<li class="c0"><span class="c2">super</span></li>
<li class="c0"><span class="c2">new</span></li>
</ul>
<p class="c1 c5"> </p>
<p class="c1"><span class="c2">En resumidas cuentas, DataBinding nos aporta otra herramienta para evitar mucho c&oacute;digo repetitivo en las clases y que meramente es usado para actualizar valores, establecerlos, etc en la UI. Como siempre mantengo, se debe usar con cabeza y no abusar de esta potente librer&iacute;a que nos puede simplificar el desarrollo de UI de forma considerable. Espero que os haya gustado.</span></p>
<p class="c1 c5"> </p>]]></content:encoded>
</item><item>
<title><![CDATA[Agilizando procesos en desarrollos con WooCommerce]]></title>
<link>https://betabeers.com/blog/agilizando-procesos-desarrollos-woocommerce-369/</link>
<pubDate>Fri, 08 Jun 2018 08:02:10 +0000</pubDate>
<category>Colaboraciones</category>
<content:encoded><![CDATA[<p>Quiz&aacute;s sea el &uacute;nico, y me pod&aacute;is tomar por loco, pero creo sinceramente que muchos os ver&eacute;is reflejados en lo que voy a contar a continuaci&oacute;n.</p>
<p>Cada vez utilizamos m&aacute;s <strong>WooCommerce como n&uacute;cleo en el desarrollo de nuestros proyectos</strong> con WordPress.</p>
<p>Atr&aacute;s quedaron los tiempos en los que crear una web corporativa con una secci&oacute;n de noticias y un formulario de contacto era todo un reto. Cada vez m&aacute;s, <strong>los requerimientos de los proyectos web que encargan nuestros clientes son mayores</strong>, se solicitan m&aacute;s funcionalidades y la compra online de productos y servicios es un elemento a tener en cuenta.</p>
<p style="text-align: center;"><img title="Agilizando procesos desarrollo WooCommerce" src="http://betabeers.com/uploads/blog/20180608_agilizando-procesos-desarrollos-woocommerce.jpg" alt="Agilizando procesos WooCommerce" width="600" height="400" /></p>
<p style="text-align: left;">Por este motivo, <strong>WooCommerce como sistema de venta online con WordPress</strong>, est&aacute; convirti&eacute;ndose en la base que sostiene multitud de proyectos.</p>
<p>Vamos a intentar ofrecer algunas herramientas y consejos que nos servir&aacute;n para poder <strong>trabajar de manera m&aacute;s eficaz con WooCommerce y automatizar muchos de los procesos</strong> que en un primer momento podr&iacute;an parecernos tediosos.</p>
<h2>Trabajando desde la terminal con WooCommerce</h2>
<p>Hemos tratado en alguna ocasi&oacute;n la herramienta <a href="../gestion-avanzada-wordpress-wp-cli-302/">WP-CLI</a>que nos permite manejar de manera &aacute;gil muchos de los procedimientos de instalaci&oacute;n de WordPress. En este caso nos encontramos con <a href="https://github.com/woocommerce/woocommerce/wiki/WC-CLI-Overview">WC CLI</a>, que bajo el mismo fundamento permite incluir <strong>procesos relacionados con WooCoommerce</strong>.</p>
<p>Disponemos de m&aacute;s de una veintena de &oacute;rdenes con multitud de variables a tener en cuenta, que nos permiten gestionar gran parte de nuestros desarrollos con WooCommerce desde la consola de comandos e incluso crear tareas automatizadas combinando esos comandos. Algunas de las &oacute;rdenes que podemos realizar desde la terminal son las siguientes:</p>
<p><strong>wc customer</strong></p>
<p>Muestra informaci&oacute;n de un usuario grupos de usuarios dentro de una instalaci&oacute;n de WooCoommerce. Adem&aacute;s permite gestionar acciones contra usuarios creados en la base de datos, o actualizar datos de manera &aacute;gil</p>
<p><strong>wc customer_download</strong></p>
<p>Nos permite gestionar la descarga de listados de clientes utilizando diferentes par&aacute;metros de selecci&oacute;n</p>
<p><strong>wc product_cat</strong></p>
<p>Principalmente &uacute;til para obtener listados de productos en funci&oacute;n de una categor&iacute;a que hayamos facilitado previamente con la orden. Tambi&eacute;n permite crear y actualizar categor&iacute;as de productos existentes.</p>
<p><strong>wc product</strong></p>
<p>Permite listar productos, crearlos o incluso actualizar sus caracter&iacute;sticas con tan solo una orden desde la terminal</p>
<p>Para poder tener una referencia m&aacute;s exacta de las posibilidades os dejo algunos ejemplos que nos pueden hacer una idea de las posibilidades de interacci&oacute;n que nos ofrece WC CLI.</p>
<p><strong>Generar un listado de pedidos</strong></p>
<p>wp wc order list</p>
<p><strong>Crear un nuevo cliente</strong></p>
<p>wp wc customer create --email='woo@woo.local' --user=1 --billing='{"first_name":"Bob","last_name":"Tester","company":"Woo", "address_1": "123 Main St.", "city":"New York", "state:": "NY", "country":"USA"}' --shipping='{"first_name":"Bob","last_name":"Tester","company":"Woo", "address_1": "123 Main St.", "city":"New York", "state:": "NY", "country":"USA"}' --password='hunter2' --username='mrbob' --first_name='Bob' --last_name='Tester'</p>
<p><strong>Listar los datos de un cliente en formato CSV</strong></p>
<p>wp wc customer get 17 --user=1 --format=csv</p>
<p><strong>A&ntilde;adir una nota para un cliente</strong></p>
<p>wp wc order_note create 355 --note="Great repeat customer" --customer_note=true --user=1</p>
<p><strong>Actualizar un cup&oacute;n previamente creado</strong></p>
<p>wp wc shop_coupon update 45 --amount='10' --discount_type='percent' --free_shipping=true --user=1</p>
<p><strong>Eliminar la cach&eacute; de WooCommerce</strong></p>
<p>wp wc tool run clear_transients --user=1</p>
<p>Si necesitas echar mano de la documentaci&oacute;n en este enlace podr&aacute;s conocer cuales son todas las <a href="%20https:/github.com/woocommerce/woocommerce/wiki/WC-CLI-Commands">&oacute;rdenes disponibles para WC CLI</a>.</p>
<h2>Algunos plugins esenciales para agilizar WooCommerce</h2>
<h3>WP All Import</h3>
<p>Uno de los mayores quebraderos de cabeza para tiendas online con grandes cat&aacute;logos de productos es la dificultad con la que nos podr&iacute;amos encontrar a la hora de actualizar el contenido.</p>
<p>Existen herramientas gratuitas como <a href="https://es.wordpress.org/plugins/wp-all-import/">WP All Import </a>que nos ayudar&aacute;n a importar productos en nuestro ecommerce mediante la simple subida de un fichero CSV o XML. Vamos, de este modo, a poder mantener actualizado nuestro stock de productos y &uacute;ltimas referencias con tan solo unos sencillos pasos.</p>
<p>Adem&aacute;s, si requerimos un uso exhaustivo de la herramienta, tambi&eacute;n podremos utilizar la <a href="http://www.wpallimport.com/%20">versi&oacute;n premium de WP All Import</a> que permite automatizar el proceso de subida de nuevos productos o actualizaci&oacute;n de los mismos. Tambi&eacute;n podremos crear tareas recurrentes, incluir im&aacute;genes en las subidas o importar productos desde el archivo contenido en una direcci&oacute;n URL:</p>
<h3>AutomateWoo</h3>
<p>Una vez que hemos podido automatizar de manera sencilla la importaci&oacute;n de productos y actualizaci&oacute;n del stock de nuestro WooCommerce podemos pensar en otro tipo de automatizaciones posibles.</p>
<p>Cualquier proceso que podamos imaginar puede llegar a ser automatizado, y a veces, la soluci&oacute;n pasa por ahorrar tiempo y gestionar eficazmente peque&ntilde;as tareas.</p>
<p>Con <a href="https://automatewoo.com/">AutomateWoo </a>tenemos un interesante compendio de automatizaciones que ayudar&aacute;n a potenciar nuestro sitio web. Algunas de las caracter&iacute;sticas son las siguientes:</p>
<ul>
<li>Emails de seguimiento para fidelizar a los clientes o incentivar futuras compras.</li>
<li>Recuperaci&oacute;n de carritos abandonados con el fin de cerrar ventas que fueron iniciadas.</li>
<li>Cupones personalizados para cada cliente con ofertas exclusivas orientadas a sus intereses.</li>
<li>Recomendaciones de productos similares o relacionados con aquellos que ya se han adquirido previamente o por los que el usuario ha mostrado inter&eacute;s.</li>
<li>Recompensas por la valoraci&oacute;n de productos aportando contenidos e incentivando la participaci&oacute;n en el sitio web.</li>
<li>Recordatorio de fechas de caducidad de tarjetas de cr&eacute;dito para evitar que una futura compra pueda llegar a ser infructuosa.</li>
</ul>
<h2>Conclusiones finales</h2>
<p>Cuando trabajamos con WooCommerce tenemos a nuestro alcance <strong>multitud de herramientas que nos permiten vender online cualquier tipo de servicio</strong> que podamos imaginar. Reservas de alojamientos, contenidos bajo suscripci&oacute;n o, simplemente, tiendas online de productos f&iacute;sicos.</p>
<p>La complejidad del proyecto puede hacer que muchas de las horas de las que disponemos las tengamos que utilizar en desarrollar el propio proyecto, dejando de lado otras tareas que pueden derivar en el &eacute;xito o fracaso del propio ecommerce. Por ese motivo es vital <strong>aprender a ser eficaces y ahorrar tiempo en tareas</strong> que son repetitivas o pueden ser optimizadas.</p>
<p>Pensemos en todo aquello que es un lastre en nuestros desarrollos y dejemos que pueda ser optimizado para <strong>que los desarrollos trabajen por nosotros</strong> y nos dediquemos a lo que de verdad importa.</p>]]></content:encoded>
</item></channel></rss>