16 minutos de lectura ( 3107 palabras)

Anexando un página php externa e integrándola con el API

En esta publicación abordaremos los procedimientos necesarios para anexar una página escrita en php a joomla. La utilidad de esta técnica consiste en la posibilidad de generar pequeñas aplicaciones personalizadas como formularios, reportes o integraciones con otros softwares.

El único inconveniente de esta práctica es que al momento de actualizar el CMS es posible perder el trabajo realizado. Es mucho mejor crear una extensión que cumplan con las normas de desarrollo, pero ese es un tema que veremos más adelante.

En fin, regresando a nuestro proyecto y para un mejor entendimiento, lo dividiremos en 3 fases: Diseño, codificación e integración. 

Diseño

Comenzaremos por decidir qué propósito tendrá nuestra mini-aplicación. Le propongo el caso de una encuesta que detecte el usuario automáticamente y además sea con campos condicionados. Esto significa que dependiendo del valor elegido en algunos casos, variará la secuencia y valores del siguiente campo de elección. Para este ejemplo ocuparemos las tecnologías PhpMySqlAjax y JavaScript (en ambos casos, basados en mootools que es el framework de JavaScript nativo de joomla).

De esta forma tendremos un directorio en la raiz de nuestro sitio con el nombre de "app". Dentro del él, veremos los archivos: index.php, ajax_rsp.php, fnc.js e index.html, este último es solo para seguridad.

Abordaremos primero los archivos de menor complejidad hasta llegar a el mas complicado que es el index.php. El primer archivo que analizaremos será ajax_rsp.php. Este elemento es muy sencillo y constituye la respuesta a la consulta ajax que haremos en el archivo principal. De esta forma, lo que haremos es crear un formulario que consulta el género del usuario y dependiendo de esto, despliega una pregunta. al enviar el formulario, se muestra el nombre del usuario, sus datos de respuesta y el tiempo que ha estado en la página.

Trabajaremos con un formulario sencillo y sin estilos dado que la importancia, esta vez, radica en el código. El formulario final se ve como las siguientes imágenes:

Formulario Inactivo

Formulario al seleccionar el género 

Formulario al enviar los datos

Observe que se muestra el nombre del usuario que se ha logueado en el sistema

Detalle donde se muestra el tiempo de sesión ocupado 

Como mencionábamos, el primer análisis lo haremos sobre el archivo que trae la respuesta de la petición ajax. Es código el sencillo y sólo muestra un campo input con una etiqueta que cambia dependiendo si la variable recibida indica masculino o femenino. 

El segundo archivo es fnc.js. En esta librería se ubicara la función de petición ajax basada en Request.HTML de mootools, el framework de JavaScript nativo de joomla!. 

El último archivo index.php donde se encuentran conjugados tres elementos, la integración con el framework del CMS, el html del formulario y la respuesta producida por el envío del formulario. 

Codificación

Veremos el código de los archivos en el mismo orden que se planteó su diseño. Comenzaremos por ajax_rsp.php

  1. <?php
  2. // función de php que transforma las variables de servidor $_REQUEST en variables de php
  3. extract($_REQUEST);
  4. if (isset($gender)) {
  5.         if ($gender = = 'F') {
  6.                 echo '<label>Escriba el nombre de un perfume<label><input type="text" name="optInpt" id="optInptId" value="" />';
  7.         } elseif ($gender = = 'M') {
  8.                 echo '<label>Escriba el nombre de un futbolista<label><input type="text" name="optInpt" id="optInptId" value="" />';
  9.         } else {
  10.                 echo 'Seleccione un valor';
  11.         }
  12. }
  13. ?>


Tal como habíamos adelantado, observamos un código sencillo donde mediante una serie de condiciones se define que respuesta retorna la petición ajax.

Los elementos a destacar son:

extract($_REQUEST) : es una función de php que convierte las variables de super-globales en variables de Php, recordemos que algunas variables super-globales son: $_GET , $_POST , $_COOKIE, $_FILES, $_REQUEST , $_SESSION. Todas ellas son vectores o arrays en las que se definen datos. De este modo, aplicar extract implica convertir a cada elemento del array en una variable independiente. Por este motivo, decir $_POST['gender'] y $gender luego de haber aplicado extract() es lo mismo.

Veamos ahora fnc.js 

  1. // Función para envío del formulario, la idea de manejarlo de este modo es incluir validaciones 
  2. // en la función antes del envío.
  3. function fhSubmit() {
  4.     $('test-fm').submit();
  5. }
  6. // Función basada en mootols que administra las peticiones ajax
  7. function getDataFm() {
  8. // Se crea un nuevo objeto de petición
  9.     new Request.HTML({
  10. // Se define el archivo de procesamiento y respuesta
  11.         url: 'ajax_rsp.php',
  12. // Se define una acción para mostrar mientras se espera la respuesta
  13.         onRequest: function(){
  14.             $('dspInput').set('text', 'Estamos procesando su respuesta...');
  15.         },
  16. // Se define la acción de respuesta
  17.         onComplete: function(response){
  18.             $('dspInput').empty().adopt(response);
  19.         },
  20. // Se define las variables enviadas
  21.         data: {
  22.             // variables
  23.             gender: $('optSelId').value
  24.         }
  25. // Se envía.
  26.     }).send();
  27. }

En este caso se destaca la facilidad del manejo de ajax que nos brinda mootools mediante el objeto Request.HTML. Pueden encontrar mas información en el sitio oficial de mootools.

Finalmente veremos el código mas complejo index.php 

  1. <?php
  2. // Establecemos que se trata de un archivo principal.
  3. (!defined('_JEXEC')) ? define('_JEXEC', 1) : NULL;
  4. // Definimos una constante para el caracter / que es usado como separador
  5. (!defined('DS')) ? define('DS', DIRECTORY_SEPARATOR) : NULL;
  6. // Definimos la ruta base de nustro sitio
  7. define ('INNER_PATH', str_replace('/app', '', dirname(__FILE__)));
  8. // Incorporamos definiciones de variables y constantes globales
  9. if (file_exists(INNER_PATH . '/defines.php')) {
  10. include_once INNER_PATH . '/defines.php';
  11. }
  12. // Incorporamos las clases y objetos el framework
  13. if (!defined('_JDEFINES')) {
  14. define('JPATH_BASE', INNER_PATH);
  15. require_once JPATH_BASE . DS . 'includes' . DS . 'defines.php';
  16. }
  17. require_once JPATH_BASE . DS . 'includes' . DS . 'framework.php';
  18. // Inicializamos la aplicacion.
  19. $app = JFactory::getApplication('site');
  20. $app->initialise();
  21. // Definimos la ruta relativa para las referencias web
  22. define ('WEB_PATH', str_replace('/app', '', JURI::base(true)));
  23. //Obtenemos el usuario conectado
  24. $user = JFactory::getUser();
  25. //Obtenemos la sesion
  26. $sess = JSession::getInstance('none', array());
  27. $idSessUser = $sess->getId();
  28. ?>
  29. <!-- El siguiente es el código del formulario -->
  30. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
  31. <html xmlns="http://www.w3.org/1999/xhtml" >
  32. <head>
  33. <!-- The following JDOC Head tag loads all the header and meta information from your site config and content. -->
  34. <script src="/<?php echo WEB_PATH  . DS . 'media' . DS . 'system' . DS . 'js' . DS; ?>mootools-core.js" type="text/javascript"></script>
  35. <script src="/<?php echo WEB_PATH  . DS . 'media' . DS . 'system' . DS . 'js' . DS; ?>core.js" type="text/javascript"></script>
  36. <script src="/<?php echo WEB_PATH  . DS . 'media' . DS . 'system' . DS . 'js' . DS; ?>caption.js" type="text/javascript"></script>
  37. <script src="/<?php echo WEB_PATH  . DS . 'media' . DS . 'system' . DS . 'js' . DS; ?>mootools-more.js" type="text/javascript"></script>
  38. <script src="/<?php echo JURI::base(true) . DS; ?>fnc.js" type="text/javascript"></script>
  39. <link rel="stylesheet" href="/<?php echo JURI::base(true) . DS; ?>style.css" type="text/css">
  40. </head>
  41.     <body>
  42. <fieldset>
  43. <legend>Formulario</legend>
  44. <form name="test" id="test-fm" method="post" acction="<?php echo $_SERVER['PHP_SELF']; ?>">
  45. <label>Seleccione su G&eacute;nero<label>
  46. <select name="optSel" id="optSelId" onChange="javascript:getDataFm()" >
  47. <option value="-1">-- Selecionar --</option>
  48. <option value="F">Femenino</option>
  49. <option value="M">Masculino</option>
  50. </select>
  51. <br />
  52. <div id="dspInput"></div>
  53. <input type="button" name="ok" id="sndOk" value="Enviar" onClick="javascript:fhSubmit()" />
  54. <input type="reset" name="cancel" id="cancel" value="Cancelar" />
  55. <br />
  56. </form>
  57. </fieldset>
  58. <?php
  59. $optSel  = JRequest::getVar('optSel', NULL);
  60. $optInpt = JRequest::getVar('optInpt', NULL);
  61. // Este segmento se mostrará si se produce el envío de los datos del formulario
  62. if (!empty($optSel) && !empty($optInpt)) {
  63. // esta función nos permitirá calcular el tiempo de permanencia del usuario antes del envío
  64.     function time_elapsed($secs){
  65.         $bit = array(
  66.             ' año'        => $secs / 31556926 % 12,
  67.             ' semanas'        => $secs / 604800 % 52,
  68.             ' día'        => $secs / 86400 % 7,
  69.             ' hora'        => $secs / 3600 % 24,
  70.             ' minuto'    => $secs / 60 % 60,
  71.             ' segundo'    => $secs % 60
  72.         );
  73.         foreach($bit as $k => $v){
  74.             if($v > 1)$ret[] = $v . $k . 's';
  75.             if($v == 1)$ret[] = $v . $k;
  76.         }
  77.         array_splice($ret, count($ret)-1, 0, 'y');
  78.         $ret[] = 'atrás.';
  79.         return join(' ', $ret);
  80.     }
  81. // Aquí comienza lo bueno, Vamos a utilizar algunos objetos del API ya que lo hemos instanciado
  82. // Establezcamos la conexión a la base de datos y la consulta para rescatar el tiempo de el último
  83. // refresco de página. este dato se guarda en la tabla #__session en la columna time
  84.     $db     = JFactory::getDBO();
  85.     $qry    = $db->getQuery(true)
  86.                         ->select('time')
  87.                         ->from('#__session')
  88.                         ->where('session_id = ' . $db->quote($idSessUser) . ' OR userid = ' . $db->quote($user->id));
  89.     $db->setQuery($qry);
  90.     $startSess  = $db->loadResult();
  91.     $now        = time();
  92.     $timeSpend  = time_elapsed($now - $startSess);
  93. ?>
  94. <fieldset>
  95.      <legend>Respuesta</legend>
  96.      <h2>Gracias por su respuesta <?php echo $user->name;?></h2>
  97.      <p>Confirmamos que los datos enviados son:</p>
  98.      <p>G&eacute;nero: <?php echo ($optSel=='F')? 'Femenino' : 'Masculino'; ?></p>
  99.      <p>Nombre de elemento: <?php echo $optInpt; ?></p>
  100.      <p><?php echo 'Usted accedió a esta página hace '. $timeSpend; ?></p>
  101. </fieldset>
  102. <?php
  103.  }
  104. ?>
  105. </body>
  106. </html>

Tal como lo habíamos comentado este es el elemento mas complejo y podemos notar claramente las 3 división de integración, formulario y respuesta.

Integración 

  1. <?php
  2. // Establecemos que se trata de un archivo principal.
  3. (!defined('_JEXEC')) ? define('_JEXEC', 1) : NULL;
  4. // Definimos una constante para el caracter / que es usado como separador
  5. (!defined('DS')) ? define('DS', DIRECTORY_SEPARATOR) : NULL;
  6. // Definimos la ruta base de nustro sitio
  7. define ('INNER_PATH', str_replace('/app', '', dirname(__FILE__)));
  8. // Incorporamos definiciones de variables y constantes globales
  9. if (file_exists(INNER_PATH . '/defines.php')) {
  10. include_once INNER_PATH . '/defines.php';
  11. }
  12. // Incorporamos las clases y objetos el framework
  13. if (!defined('_JDEFINES')) {
  14. define('JPATH_BASE', INNER_PATH);
  15. require_once JPATH_BASE . DS . 'includes' . DS . 'defines.php';
  16. }
  17. require_once JPATH_BASE . DS . 'includes' . DS . 'framework.php';
  18. // Inicializamos la aplicacion.
  19. $app = JFactory::getApplication('site');
  20. $app->initialise();
  21. // Definimos la ruta relativa para las referencias web
  22. define ('WEB_PATH', str_replace('/app', '', JURI::base(true)));
  23. //Obtenemos el usuario conectado
  24. $user = JFactory::getUser();
  25. //Obtenemos la sesion
  26. $sess = JSession::getInstance('none', array());
  27. $idSessUser = $sess->getId();
  28. ?>

El código que vincula la hoja suelta de php al API del CMS, se extiende desde la línea 1 a la 26. Todas las hojas anexas que quiera agregar deben llevarlo. Se debe tener cuidado con la sentencia de la línea 9

define ('INNER_PATH', str_replace('/app', '', dirname(__FILE__)));

La sentencia  dirname(__FILE__), entrega la ruta completa del directorio actual, como nosotros creamos una carpeta con el nombre app tendríamos una ruta con un patrón similar a: <mi directorio raíz>/app. Luego, como lo único que deseo es el directorio raíz aplico str_replace('/app', '', dirname(__FILE__)). Con esto reemplazo la cadena app por nada. 

En un ejemplo sobre un hosting cuyo usuario es planeta y recordando que hemos creado una carpeta app donde se encuentran los archivos de esta publicación, tendríamos que lo que ocurre es lo siguiente:

dirname(__FILE__): Retorna algo como lo siguiente /home/planeta/public_html/app

Luego:

str_replace('/app', '', dirname(__FILE__)): Reemplaza la cadena /app por nada (NULL) quedando sólo la ruta del directorio raíz /home/planeta/public_html

Finalmente:

define ('INNER_PATH', str_replace('/app', '', dirname(__FILE__))): Crea una constante INNER_PATH con la ruta del directorio raíz /home/planeta/public_html

De las líneas 24 a la 34 ocupo objetos del farmework del CMS que tiene gran poder. El método estático getUser de la clase JFactory, me entrega todos los datos del usuario que ha iniciado su sesión logueandose en el sitio. De este modo, cada usuario verá su propio nombre al desplegar la variable $user->name.

Las sentencias $sess = JSession::getInstance('none', array()) y $idSessUser = $sess->getId(), son las encargadas de entregar el id de sesión iniciada por usuario logueado. Con este dato y mediante una serie de otras sentencias, obtendré la hora en que el usuario aterrizó en la página. Este tiempo se renueva si se actualiza el navegador.

Si les interesa profundizar en el framework (API), pueden visitar el siguiente sitio: http://doc.joomladev.eu/api25/

Formulario 

  1. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
  2. <html xmlns="http://www.w3.org/1999/xhtml" >
  3. <head>
  4. <!-- The following JDOC Head tag loads all the header and meta information from your site config and content. -->
  5. <script src="/<?php echo WEB_PATH  . DS . 'media' . DS . 'system' . DS . 'js' . DS; ?>mootools-core.js" type="text/javascript"></script>
  6. <script src="/<?php echo WEB_PATH  . DS . 'media' . DS . 'system' . DS . 'js' . DS; ?>core.js" type="text/javascript"></script>
  7. <script src="/<?php echo WEB_PATH  . DS . 'media' . DS . 'system' . DS . 'js' . DS; ?>caption.js" type="text/javascript"></script>
  8. <script src="/<?php echo WEB_PATH  . DS . 'media' . DS . 'system' . DS . 'js' . DS; ?>mootools-more.js" type="text/javascript"></script>
  9. <script src="/<?php echo JURI::base(true) . DS; ?>fnc.js" type="text/javascript"></script>
  10. <link rel="stylesheet" href="/<?php echo JURI::base(true) . DS; ?>style.css" type="text/css">
  11. </head>
  12.     <body>
  13. <fieldset>
  14. <legend>Formulario</legend>
  15. <form name="test" id="test-fm" method="post" acction="<?php echo $_SERVER['PHP_SELF']; ?>">
  16. <label>Seleccione su G&eacute;nero<label>
  17. <select name="optSel" id="optSelId" onChange="javascript:getDataFm()" >
  18. <option value="-1">-- Selecionar --</option>
  19. <option value="F">Femenino</option>
  20. <option value="M">Masculino</option>
  21. </select>
  22. <br />
  23. <div id="dspInput"></div>
  24. <input type="button" name="ok" id="sndOk" value="Enviar" onClick="javascript:fhSubmit()" />
  25. <input type="reset" name="cancel" id="cancel" value="Cancelar" />
  26. <br />
  27. </form>
  28. </fieldset>
  29. </body>
  30. </html>

El formulario en si no tiene mucha complejidad ni tampoco es algo muy especial. El elemento mas destacable es la capa <div id="dspInput"></div> ya que ella será la receptora de la respuesta de la petición ajax. ¿Recuerdan la instrucción del archivo fnc.js: $('dspInput').empty().adopt(response);?

Pues bien, la respuesta de ajax cargará el input con la etiqueta correspondiente según el tipo de género elegido en el formulario.

Respuesta 

  1. <?php
  2. $optSel  = JRequest::getVar('optSel', NULL);
  3. $optInpt = JRequest::getVar('optInpt', NULL);
  4. // Este segmento se mostrará si se produce el envío de los datos del formulario
  5. if (!empty($optSel) && !empty($optInpt)) {
  6. // esta función nos permitirá calcular el tiempo de permanencia del usuario antes del envío
  7.     function time_elapsed($secs){
  8.         $bit = array(
  9.             ' año'        => $secs / 31556926 % 12,
  10.             ' semanas'        => $secs / 604800 % 52,
  11.             ' día'        => $secs / 86400 % 7,
  12.             ' hora'        => $secs / 3600 % 24,
  13.             ' minuto'    => $secs / 60 % 60,
  14.             ' segundo'    => $secs % 60
  15.         );
  16.         foreach($bit as $k => $v){
  17.             if($v > 1)$ret[] = $v . $k . 's';
  18.             if($v == 1)$ret[] = $v . $k;
  19.         }
  20.         array_splice($ret, count($ret)-1, 0, 'y');
  21.         $ret[] = 'atrás.';
  22.         return join(' ', $ret);
  23.     }
  24. // Aquí comienza lo bueno, Vamos a utilizar algunos objetos del API ya que lo hemos instanciado
  25. // Establezcamos la conexión a la base de datos y la consulta para rescatar el tiempo de el último
  26. // refresco de página. este dato se guarda en la tabla #__session en la columna time
  27.     $db     = JFactory::getDBO();
  28.     $qry    = $db->getQuery(true)
  29.                         ->select('time')
  30.                         ->from('#__session')
  31.                         ->where('session_id = ' . $db->quote($idSessUser) . ' OR userid = ' . $db->quote($user->id));
  32.     $db->setQuery($qry);
  33.     $startSess  = $db->loadResult();
  34.     $now        = time();
  35.     $timeSpend  = time_elapsed($now - $startSess);
  36. ?>
  37. <fieldset>
  38.      <legend>Respuesta</legend>
  39.      <h2>Gracias por su respuesta <?php echo $user->name;?></h2>
  40.      <p>Confirmamos que los datos enviados son:</p>
  41.      <p>G&eacute;nero: <?php echo ($optSel=='F')? 'Femenino' : 'Masculino'; ?></p>
  42.      <p>Nombre de elemento: <?php echo $optInpt; ?></p>
  43.      <p><?php echo 'Usted accedió a esta página hace '. $timeSpend; ?></p>
  44. </fieldset>
  45. <?php
  46.  }
  47. ?>

Finalmente, la respuesta del envío del formulario, usa muchos elementos interesantes. El primero es el uso de objetos del framework para obtener las variables superglobales, algo similar a extract() pero mucho mas sofisticado. La sentencia $optSel  = JRequest::getVar('optSel', NULL) asigna a la variable $optSel el valor de $_POST['optSel'], si esta variable no está definida, tomará el valor NULL. El objeto JRequest::getVar es el que permite esta acción. Este elemento es flexible y permite ser parametrizado del siguiente modo:

JRequest::getVar( <nombre de la variable> , <valor por defecto> ,<tipo (post, get, etc) > ,<tipo de dato (interger, string, etc)> ,<validaciones> )

Los valores que se pueden definir como validaciones se definen agregando una de las siguientes constantes:

JREQUEST_NOTRIM - previene la eliminación de espacios en blanco

JREQUEST_ALLOWRAW - Salta los filtros y restricciones de caracteres

JREQUEST_ALLOWHTML - permite más HTML. Si no se pasa, el HTML es limpiado por defecto.

Otro elemento de interés es la función  time_elapsed($secs) ya que nos permitirá calcular el tiempo de permanencia en página

Finalmente, el elemento mas significativo es JFactory::getDBO(), con esta sentencia se establece la conexión a la base de datos y además se crea el objeto $db que consta de múltiples métodos de consulta y despliegue de resultados, como se puede apreciar en de las líneas 39 a 46. 

Integración

Para cerrar viene el proceso mas sencillo que consiste en decirle al CMS que muestre nuestro formulario. En esta ocasión elegí crear un nuevo ítem de menú en el menú principal del tipo "Página embebida" o "wrapper". De este modo, vamos al administrador de menú, seleccionamos agregar ítem, seleccionamos el tipo y agregamos la URL donde se ubica el index.php, en el ejemplo sería algo como http://misitio/app/index.php

Los resultados podemos verlos en las siguientes imágenes:

 





Bien amigos eso es todo, los invito a quedar pendientes de nuevas publicaciones donde explicaré la creación de extensiones para Joomla! 

0
Elige tu extensión favorita: Noviembre
Mejoras para JLayout en Joomla! 3.2
 

Comentarios

¿Ya està registrado? Ingresa Aquí
No hay comentarios por el momento. Sé el primero en enviar un comentario.

By accepting you will be accessing a service provided by a third-party external to https://magazine.joomla.org/