Hoy mismo he necesitado de una validación para verificar si un dato ya existe en la base de datos. Dado que CakePHP no lleva una validación en su núcleo para estos menesteres he tenido que crearla y ahora la comparto con vosotros:
/**
* Verifica si un campo existe en la base de datos
* Si estamos editando se excluye el valor guardado en la validación
* @param array $data
* @param string $field Nom de la cel·la a verificar
* @return
*/
public function checkUnique($data)
{
$field = array_pop(array_keys($data));
$data = array_pop(array_values($data));
// Si estem editant...
if(isset($this->data[$this->name]['id']))
{
if ($field == $this->field($field, array($this->name . '.id' => $this->data[$this->name]['id'])))
{
return true;
}
}
// Si no estem editant...
if($this->hasField($field)) {
return $this->isUnique(array($field => $data));
}
}
Para utilizarlo no tenéis más que añadir el trozo de código en el modelo deseado (normalmente en AppModel, para poder utilizarlo desde cualquier modelo) y añadir la validación en el modelo que queráis:
var $validate = array('NOMBRE DE LA CELDA A VALIDAR' => array(
'rule' => 'checkUnique',
'message' => 'Blahblahblah'));
Un ejemplo muy común:
var $validate = array('username' => array(
'rule' => 'checkUnique',
'message' => 'Ya existe un usuario registrado con este nombre!'));
Que lo disfrutéis
Edición: Se me olvidó comentar que, a diferencia del método de validación checkUnique que se utiliza en el post Inegrando CakePHP y PhpBB 3.x, este método sirve también a la hora de editar un ítem ya que, en ese caso, retorna true si el valor de la celda coincide con el ya guardado en la base de datos.
Podéis ver, como ejemplo, la página de underave. Pero os pido por favor que no os registréis si no vais a utilizar la cuenta.
Antes de empezar con el tutorial, por favor, leeros el tutorial anterior sobre el registro de usuarios externo a phpBB3, ya que habrá ciertos aspectos que pasaré por alto por estar explicados en ese tutorial.
Además de miraros ese tutorial deberíais echar un vistazo a la documentación del componente Auth, ya que sin él el registro de usuarios sería otra cosa…
Componente de integración de phpBB3 (que ahora crearemos)
Aunque yo haya utilizado la versión 3.0.2 de phpBB, debéis saber que este sistema debe funcionar igual de bien en cualquier versión de phpBB 3
Parto de la base de que tenéis creado un modelo y un controlador encargados de gestionar los usuarios de vuestra aplicación Cake. En mi caso los he llamado “users“, aunque podéis llamarlos “usuarios”, “members“, o como os dé la gana.
Es decir, doy por supuesto que ya tenéis un sistema de usuarios funcionando en vuestra aplicación CakePHP. En este tutorial sólo os explicaré cómo integrar el registro y login de phpBB3, así que todo lo demás (validaciones de datos, creación de formularios, funcionamiento de la clase Auth…) quedará por supuesto (lo cual no quiere decir que no vaya a haber código al respecto ).
Además de la parte encargada de la gestión de usuarios vamos a tener que crear un modelo para el foro. Este sólo debe contener el nombre del modelo y debemos indicarle que no va a utilizar base de datos:
<?php // /app/models/forum.php
class Forum extends AppModel
{
var $name = 'Forum';
var $useDbConfig = 'forums';
var $useTable = false;
}
Como podéis ver, además de haber indicado el nombre del modelo y haber desactivado el uso de base de datos en éste, he indicado que utilice la configuración de base de datos forums. Con esto indicamos al modelo que, en caso de utilizar la base de datos, la configuración que utilizaremos será la forums.
Podéis añadir tantas configuraciones de conexión a la base de datos como queráis, simplemente cread una variable con el nombre que queráis de conexión conteniendo los datos necesarios en el fichero /app/config/database.php:
Si vuestras tablas del foro tienen un prefijo (como es mi caso) aseguraros de especificar la opción “prefix” en el array de conexión a la base de datos.
Vamos a por la creación del componente PhpBB3 que nos permitirá el login de usuarios. El componente es una modificación de uno llamado PhpBB3 Api Bridge, del Bakery the Cake.
He modificado el componente porque tal y como lo presenta Wilson Sheldon (el autor del componente), al iniciar sesión en el foro y si el usuario no existe, se le registra en el sistema.
Esto está muy bien cuando añadimos el foro después de haber creado nuestra aplicación CakePHP. Sin embargo, si en lugar de añadir el foro estamos creando un portal con CakePHP y con phpBB desde cero, lo más seguro es que no os interese hacer esa verificación.
Cread, pues, el componente php_b_b3.php con el siguiente contenido:
<?php // /app/controllers/components/php_b_b3.php
/**
* Created by Willson Sheldon => http://bakery.cakephp.org/articles/view/phpbb3-api-bridge
* Modified by Òscar Casajuana a.k.a. elboletaire => http://www.underave.net
*/
class PhpBB3Component extends Object
{
var $controller;
var $model;
var $phpBBpath = '/ruta/a/tu/instalacion/de/phpBB3/';
/**
* Inicia la sesión en phpBB3
* @param string $username
* @param string $password
* @param bool $remember [optional] Recordar entre sesiones
*/
public function login($username, $password, $remember = false)
{
$this->auth->login($username, $password, $remember);
}
/**
* Cierra la sesión en phpBB
*/
public function logout()
{
$this->user->session_kill();
$this->user->session_begin();
}
/**
* Registra un usuario en el sistema
* @param array $data Datos del usuario
* @return id del usuario en caso de éxito; falso en caso contrario
*/
public function register($data)
{
// Paràmetres per defecte
// Grup usuaris registrats
if(!isset($data['group_id']) || empty($data['group_id'])) $data['group_id'] = 5;
// Franja horària GMT+01
if(!isset($data['user_timezone']) || empty($data['user_timezone'])) $data['user_timezone'] = 1;
// Horari d'estiu desactivat
if(!isset($data['user_dst']) || empty($data['user_dst'])) $data['user_dst'] = 0;
if(!isset($data['user_lang']) || empty($data['user_lang'])) $data['user_lang'] = 'es';
// Usuari inactiu per defecte
if(!isset($data['user_type']) || empty($data['user_type'])) $data['user_type'] = 1;
// Això millor no tocar-ho
if(!isset($data['user_style']) || empty($data['user_style'])) $data['user_style'] = 2;
$userData = array(
'username' => $data['username'],
'username_clean' => strtolower($data['username']),
'user_password' => $this->phpbb_hash($data['user_password']),
'user_email' => $data['user_email'],
'user_ip'=>$_SERVER['REMOTE_ADDR'],
'group_id' => $data['group_id'], //Registered users group
'user_timezone' => $data['user_timezone'],
'user_dst' => $data['user_dst'],
'user_lang' => $data['user_lang'],
'user_type' => $data['user_type'],
'user_actkey' => '',
'user_dateformat' => 'D d M Y, g:i a',
'user_style' => 2,
'user_regdate' => time(),
);
$userId = user_add($userData);
if(empty($userId)) return false;
else
{
// Actualitzem darrer usuari registrat al phpBB
update_last_username();
return $userId;
}
}
/**
* Encripta una contraseña utilizando el
* método de encriptación de phpBB3
* @param string $password
* @return contraseña encriptada
*/
public function phpbb_hash($password)
{
$itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
$random_state = $this->unique_id();
$random = '';
$count = 6;
if (($fh = @fopen ( '/dev/urandom', 'rb' )))
{
$random = fread ($fh, $count);
fclose ($fh);
}
if (strlen($random) < $count)
{
$random = '';
for($i=0;$i<$count;$i+=16)
{
$random_state = md5($this->unique_id () . $random_state);
$random .= pack('H*', md5($random_state));
}
$random = substr($random, 0, $count);
}
$hash = $this->_hash_crypt_private($password, $this->_hash_gensalt_private($random, $itoa64 ), $itoa64);
if (strlen($hash) == 34)
{
return $hash;
}
return md5($password);
}
/**
* Verifica la existencia de un usuario
* @param string $username
* @return
*/
public function userExists($username)
{
if (user_get_id_name(false, $username) == 'NO_USERS')
{
return false;
}
else
{
return true;
}
}
/**
* Carga los ficheros necesarios de phpBB3
*/
function startup(&$controller)
{
$this->controller = &$controller;
define('IN_PHPBB', true);
global $phpbb_root_path, $phpEx, $db, $config, $user, $auth, $cache, $template;
$phpbb_root_path = $this->phpBBpath;
$phpEx = substr(strrchr(__FILE__, '.'), 1);
require_once($phpbb_root_path . 'common.' . $phpEx);
$this->table_prefix = $table_prefix;
$this->auth = $auth;
$this->user = $user;
// Start session management
$this->user->session_begin();
$this->auth->acl($user->data);
this->user->setup();
require_once($phpbb_root_path .'includes/functions_user.php');
}
private function unique_id($extra = 'c')
{
static $dss_seeded = false;
global $config;
$val = $config ['rand_seed'] . microtime ();
$val = md5 ( $val );
$config ['rand_seed'] = md5 ( $config ['rand_seed'] . $val . $extra );
$dss_seeded = true;
return substr ( $val, 4, 16 );
}
/**
* Generate salt for hash generation
*/
private function _hash_gensalt_private($input,&$itoa64,$iteration_count_log2 = 6)
{
if ($iteration_count_log2 < 4 || $iteration_count_log2 > 31)
{
$iteration_count_log2 = 8;
}
$output = '$H$';
$output .= $itoa64 [min($iteration_count_log2 + ((PHP_VERSION >= 5) ? 5 : 3), 30)];
$output .= $this->_hash_encode64($input, 6, $itoa64);
return $output;
}
/**
* Encode hash
*/
private function _hash_encode64($input,$count,&$itoa64)
{
$output = '';
$i = 0;
do
{
$value = ord ( $input [$i ++] );
$output .= $itoa64 [$value & 0x3f];
if ($i < $count)
{
$value |= ord ( $input [$i] ) << 8;
}
$output .= $itoa64 [($value >> 6) & 0x3f];
if ($i ++ >= $count)
{
break;
}
if ($i < $count)
{
$value |= ord ( $input [$i] ) << 16;
}
$output .= $itoa64 [($value >> 12) & 0x3f];
if ($i ++ >= $count)
{
break;
}
$output .= $itoa64 [($value >> 18) & 0x3f];
} while ( $i < $count );
return $output;
}
/**
* The crypt function/replacement
*/
private function _hash_crypt_private($password,$setting,&$itoa64)
{
$output = '*';
// Check for correct hash
if (substr ( $setting, 0, 3 ) != '$H$')
{
return $output;
}
$count_log2 = strpos ( $itoa64, $setting [3] );
if ($count_log2 < 7 || $count_log2 > 30)
{
return $output;
}
$count = 1 << $count_log2;
$salt = substr ( $setting, 4, 8 );
if (strlen ( $salt ) !=
{
return $output;
}
/**
* We're kind of forced to use MD5 here since it's the only
* cryptographic primitive available in all versions of PHP
* currently in use. To implement our own low-level crypto
* in PHP would result in much worse performance and
* consequently in lower iteration counts and hashes that are
* quicker to crack (by non-PHP code).
*/
if (PHP_VERSION >= 5)
{
$hash = md5 ( $salt . $password, true );
do
{
$hash = md5 ( $hash . $password, true );
} while ( -- $count );
}
else
{
$hash = pack ( 'H*', md5 ( $salt . $password ) );
do
{
$hash = pack ( 'H*', md5 ( $hash . $password ) );
} while ( -- $count );
}
$output = substr ( $setting, 0, 12 );
$output .= $this->_hash_encode64 ( $hash, 16, $itoa64 );
return $output;
}
}
Aunque veáis mucho código, no os asustéis. La mayoría de estos métodos son para encriptar la contraseña. Los métodos que nosotros utilizaremos serán login, logout, register y userExists.
Lo primero que debéis hacer es substituir la ruta hacia vuestra instalación de phpBB3, en la línea 11 (subrayada en azul, como no).
Ahora que ya tenemos el componente vamos a empezar a desarrollar el registro de usuarios. En esta parte editaremos / crearemos tres ficheros: el modelo de usuarios, para añadir las validaciones correspondientes; el controlador de usuarios, para añadir el método de registro y la vista del registro de usuarios.
Los usuarios los registraremos activados para ahorrarnos complicaciones. Dejo en vuestras manos la creación de un método para la activación de la cuenta del usuario en caso de que registréis a vuestros usuarios con la cuenta inhabilitada.
Empecemos con la vista register.ctp:
<?= $form->create('User',array('action'=>'register')) ?>
<?= $form->input('User.username', array('label'=>__("Nombre de usuario",true))) ?>
<!-- Indicamos value = '' a la contraseña para que no se rellene el campo automáticamente en caso de haber algún error en los datos -->
<?= $form->input('User.password', array('label'=>__('Contraseña',true),'type'=>'password','value'=>'')) ?>
<?= $form->input('User.confirm_passwd', array('label'=>__('Confirma la contraseña',true),'type'=>'password','value'=>'')) ?>
<?= $form->input('User.email', array('label'=>__('E-mail',true))) ?>
<?= $form->end(__('Registrarse',true)) ?>
Sencillo, ¿no?
Creemos las validaciones que nos interesen en nuestro modelo user:
<?php // /app/models/user.php
class User extends AppModel {
var $name = 'User';
var $validate = array(
'username' => array(
'length' => array(
'rule' => array('between', 3, 23),
'message' =>"El nombre debe contener entre 3 y 23 caracteres"
),
'exists' => array(
'rule' => array('checkUnique','username'),
'message' => "El nombre de usuario ya existe"
)
),
'email' => array(
"El e-mail introducido no es válido" => VALID_EMAIL,
"El e-mail es obligatorio" => VALID_NOT_EMPTY,
"exists" => array(
'rule' => array('checkUnique','email'),
'message' => "Ya hay un usuario registrado con este e-mail"
)
),
'confirm_passwd' => array('identicalFieldValues' => array(
'rule'=>array('identicalFieldValues','password'),
'message'=>'Las contraseñas introducidas no coinciden'
)
),
'password' => array('length' => array(
'rule' => array('minLength', 6),
'message' =>"La contraseña debe contener al menos 6 caracteres"
)
)
);
/**
* Verifica si dos campos son iguales
*/
function identicalFieldValues( $field = array(), $compare_field = null )
{
foreach( $field as $key => $value )
{
$v1 = $value;
$v2 = $this->data[$this->name][$compare_field];
if($v1 !== $v2)
{
return FALSE;
}
else
{
continue;
}
}
return TRUE;
}
/**
* Verifica si un dato existe en la base de datos
* @param $data Dato con el que comparar
* @param $fieldName Nombre de celda a verificar
* @return bool
*/
function checkUnique($data, $fieldName )
{
$valid = false;
if(isset($fieldName) && $this->hasField($fieldName))
{
$valid = $this->isUnique(array($fieldName => $data));
}
return $valid;
}
}
Los métodos identicalFieldValues y checkUnique son los encargados de verificar si las dos contraseñas coinciden y si el usuario existe en la base de datos.
Si tenéis cualquier duda sobre la creación del modelo podéis dirigiros a la documentación de cake al respecto, ya que allí se explica todo con detalle:
Vamos a por el método register. Este puede variar mucho según la aplicación que queráis hacer… por ejemplo, en el caso de underave verifico que el usuario no está registrado ni en el foro ni en la página principal, ya que cuando hicimos el cambio cometimos el error de hacerlo así. En el ejemplo doy por supuesto que si el usuario no existe en la base de datos principal, no existirá en el foro. De todos modos veréis cómo hacer para verificar la existencia de un usuario (todo el primer trozo comentado):
// /app/controllers/users_controller.php
class UsersController extends AppController
{
var $name = 'Users';
var $components = array('Auth','PhpBB3');
function register()
{
$this->pageTitle = __("Registro",true) . " | " . __("Usuarios",true);
if(!empty($this->data))
{
// Guardamos la contraseña sin encriptar para registrar al usuario en el foro
$password = $this->data['User']['confirm_passwd'];
// Si nos interesa, verificamos la existencia del usuario en el foro
// if($this->phpBB->userExists($this->data['User']['username']))
// $this->User->invalidate('username','exists');
// Cargamos los datos del usuario en el modelo
$this->User->set($this->data);
// En caso de pasar las validaciones...
if($this->User->validates())
{
// Registramos el usuario en el foro
$datos = array(
'username' => $this->data['User']['username'],
'user_password' => $password,
'user_email' => $this->data['User']['email']
);
if($userForumId = $this->PhpBB3->register($datos))
{
// En caso de haber registrado al usuario correctamente guardamos la variable con su id del foro
$this->data['User']['forum_id'] = $userForumId;
if($this->User->save($this->data,false))
{
$this->Session->setFlash(__("Usuario registrado correctamente",true));
$this->redirect('/');
}
else
{
$this->Session->setFlash(__("Ha habido algún error al registrarte...",true));
}
}
else
{
$this->Session->setFlash(__("Ha habido algún error al registrarte...",true));
}
}
}
}
}
Como podéis ver en el ejemplo, una vez he registrado al usuario en el foro me quedo con la ID del usuario creado y es entonces cuando registro al usuario en el sistema, para poder disponer de esta ID.
Si no guardáis la ID del usuario en vuestra base de datos de Cake no podréis hacer nada una vez registrado el usuario. Es decir, no podréis hacer ni el login, ni un cambio de contraseña ni nada de nada. Así que es vital que guardéis su ID.
Bien, en principio esta parte ya la tenemos. Ahora mismo al registrarse alguien en vuestro sistema queda registrado en el foro y en la base de datos de CakePHP.
Ap!! Pero alto, si lo probáis no funcionará. Adelante, haced la prueba. Es necesaria para seguir adelante.
Si estáis utilizando caché, lo más seguro es que el primer error que veáis sea este:
Fatal error: Cannot redeclare class cache in …
Esto sucede porque el núcleo de Cake utiliza una classe que se llama igual que una del núcleo de phpBB3 (cache, en este caso). Estuve varios meses dándole vueltas a este asunto y la mejor solución que encontré fue modificar algunos ficheros de phpBB3, así que vamos a ello. No os preocupéis, tan solo modificaremos unos pocos ficheros:
Básicamente lo que hay que hacer es renombrar la clase cache. Primero modificaremos el nombre de la clase y luego pasaremos a cambiar aquellas líneas de código donde se instancíe la clase.
Abrid phpBB3/includes/cache.php y buscad lo siguiente (en la línea 23 aproximadamente):
class cache extends acm
Substituidlo por:
class fcache extends acm
Guardad el fichero y cerradlo. Ahora abrid estos ficheros:
phpBB3/common.php
phpBB3/style.php
phpBB3/download/file.php
Buscad esto en cada uno de ellos:
new cache();
Y reemplazadlo por:
new fcache();
Hecho esto (y una vez guardados los ficheros, por supuesto..) subís los ficheros al servidor y el foro debería seguir funcionando correctamente.
¡Aps! ¿Que no os funciona correctamente? Si, como yo, habéis creado el sistema de usuarios utilizando la palabra “users”, tendréis otro error fatal:
Fatal error: Cannot redeclare class user…
Del mismo modo que con la caché, la mejor forma de solucionar esto es modificando phpBB3. Abrid el fichero phpBB3/includes/session.php y buscad lo siguiente (en la línea 1376 aproximadamente):
class user extends session
Y reemplazadlo por…
class fuser extends session
Ahora abrid el fichero phpBB3/common.php y buscad lo siguiente:
new user();
Y reemplazadlo por:
new fuser();
Ahora sí que sí . Vuestro registro de usuarios, así como vuestra instalación de phpBB, deberían funcionar perfectamente
Bien, ahora pasaremos a la creación de la vista login.ctp:
<?php // /app/views/users/login.ctp
echo $form->create('User',array('action'=>'login'));
echo $form->input('User.username', array('label'=>__("Nombre",true)));
// Utilizamos 'pass' para que Auth no nos encripte la contraseña automáticamente
echo $form->input('User.pass', array('label'=>__('Contraseña',true), 'type'=>'password'));
// Guardamos el referer para saber si el usuario viene del foro y así poderle enviar de nuevo al finalizar el login
echo $form->hidden('User.referer',array('value'=>@$_SERVER['HTTP_REFERER']));
// Nota: Recordar entre sesiones funciona en el foro. Si no tenéis algún componente o método para recordar sesiones en Cake, quizás os interese utilizar alguno.
echo $form->input("User.remember_me",array('type'=>'checkbox','label'=>__("Recordar entre sesiones",true)));
echo $form->end(__('Iniciar sesión',true));
?>
Es importante que guardemos la contraseña con un nombre de campo distinto a password ya que necesitamos la contraseña sin encriptar para poder iniciar sesión en el foro.
Pasemos al método login de nuestro controlador de usuarios. Ya que estamos, también añadiremos el de logout:
// /app/controllers/users_controller.php
function login()
{
if (!empty($this->data))
{
// Guardamos la contraseña encriptada para iniciar sesión en Cake
$this->data['User']['password'] = $this->Auth->password($this->data['User']['pass']);
// Iniciamos sesión en Cake
if ($this->Auth->login($this->data))
{
// Iniciamos sesión en PhpBB. Si tenemos el campo 'remember_me' se lo pasamos como tercer parámetro.
$this->PhpBB3->login($this->data['User']['username'], $this->data['User']['pass'], $this->data['User']['remember_me']);
$this->Session->setFlash('Sessió iniciada correctament','flash_info',array(),'auth');
// Verifico si el usuario viene del dominio principal
if(!preg_match('/^http:\/\/(www\.)?underave\.net/', $this->data['User']['referer']) && !empty($this->data['User']['referer']))
{
// Limpio la URL de posibles ids de sesión del foro (para evitar que éste cierre la sesión por seguridad)
$referer = preg_replace('/^(.+)sid=[0-9a-z]+/i', '$1', $this->data['User']['referer']);
// Si viene del dominio principal lo redirigiremos con el método Auth->redirect()
}
else $referer = $this->Auth->redirect();
$this->redirect($referer);
}
}
}
function logout()
{
$this->PhpBB3->logout();
$this->redirect($this->Auth->logout());
}
En las líneas 13 a 17 lo que hago es verificar si el usuario viene de mi dominio principal. Lo hago así porque en mi caso tengo los foros en un subdominio. Si no es vuestro caso deberéis modificar la expresión regular para que se ajuste a vuestras necesidades.
Si, como yo, tenéis los foros en un subdominio debéis saber que necesitáis tener las cookies configuradas para que funcionen entre vuestros subdominios y vuestro dominio principal. Para hacerlo, primero de todo id a vuestro panel de control del foro. En la barra lateral de la pestaña “General” veréis un grupo de opciones: “Configuración del servidor”. Ahí dentro tenéis un enlace para acceder a la configuración de las cookies.
Dentro de esta configuración, debéis poner como dominio de las cookies un punto seguido de vuestro nombre de dominio. En mi caso .underave.net. El punto delante del dominio es importante porque algunos navegadores no reconocerían los subdominios sin él.
En directorio de la cookie aseguraros que apunta a la raíz del dominio: /
Ahora debéis aseguraros que en vuestro php.ini tenéis las cookies también configuradas como en el foro:
; The path for which the cookie is valid.
session.cookie_path = /
; The domain for which the cookie is valid.
session.cookie_domain = .underave.net
Hecho esto y subido vuestro php.ini al servidor (antes de subirlo quizás os interese echar un ojo al resto de opciones para ver qué se cuece por ahí) el login de usuarios ya debe funcionar sin problema alguno
Y si el usuario quisiera cambiar alguno de sus datos?arriba
A mi parecer, es importante que el usuario no tenga que actualizar sus datos varias veces. Es decir, si el usuario cambia su e-mail o su contraseña desde nuestra aplicación Cake, es importante que éste dato se actualice también en el foro.
Además, sería interesante modificar todos los enlaces de phpBB del perfil de usuario que tengan que ver con la edición de datos personales para que éstos enlacen hacia nuestra aplicación Cake.
Si antes he creado una conexión hacia nuestra base de datos de phpBB así como un modelo llamado Forum ha sido porque a partir de ahora trabajaremos con la base de datos en lugar de con el componente. ¿Porqué? Porque así veis que todo esto que hemos estado haciendo es posible hacerlo sin utilizar los métodos del núcleo de phpBB3. ¿Todo? Bueno no, todo no. Para el login de usuarios seguramente será necesario que utilicéis el componente (a no ser que intentéis crear a mano las cookies vosotros mismos).
El método que crearemos ahora será para cambiar la contraseña ya que el cambio de contraseña quizás sea de los datos del perfil más difíciles de modificar. Después de ver el ejemplo añadid tantos métodos como necesitéis (cambio de e-mail, de avatar …).
Para cambiar cualquier dato de los usuarios del foro sería interesante tener un método en nuestro modelo Forum:
// /app/models/forum.php
/**
* Actualiza un dato ($field) de un usuario a
* partir de su $id en el foro con el valor $value
*
* @param integer $id of user
* @param string $field to update
* @param mixed $value
* @return boolean true on success, false on failure
*/
function setUserField($id, $field, $value)
{
$this->setSource('users');
$this->primaryKey = 'user_id';
$this->id = $id;
return $this->saveField($field, $value);
}
Y aquí viene el método de cambiar contraseña (está creado en el controlador usuarios pero no estaría de más separarlo un poco entre modelo y controlador):
// /app/controllers/users_controller.php
function changePassword()
{
if (!empty($this->data)){
// Buscamos el usuario actual en la base de datos
$user = $this->User->findById($this->Auth->user('id'));
// Cargamos los datos en el modelo (para validarlos)
$this->User->set($this->data);
// Encriptamos la contraseña antigua para validarla
$this->data['User']['old_passwd'] = $this->Auth->password($this->data['User']['old_passwd']);
// Validamos y verificamos que la contraseña introducida coincide con la de la BDD
if ($this->User->validates() && $this->data['User']['old_passwd'] == $user['User']['password']) {
$password = $this->data['User']['password'];
// Actualizamos la contraseña del foro
$this->Forum->setUserField($user['User']['forums_id'], 'user_password', $this->Auth->password($this->data['User']['password']));
// Actualizamos la contraseña del sistema
$this->User->updateAll(array('User.password'=>"'".$this->Auth->password($password)."'"),array('User.id'=>$user['User']['id']));
$this->Session->setFlash(__("Tu contraseña ha sido cambiada con éxito",true),'flash_info');
$this->redirect(array('controller'=>'users','action'=>'profile'));
}
// Si la contraseña no coincide con la guardada en la BDD mostramos error.
if($this->data['User']['old_passwd'] != $user['User']['password'])
{
$this->User->invalidate('old_passwd',__("La contraseña no es correcta",true));
}
}
}
Con esto ya podéis cambiar la contraseña en todo el sistema y ya sabéis cómo hacer para modificar el resto de datos. Al actualizar la contraseña he utilizado user_password como nombre de celda. Consultad vuestra base de datos de phpBB para saber los nombres de las celdas a actualizar.
Si quisierais utilizar la vía del componente y así no tener que utilizar la base de datos y el modelo Forum tendríais que añadir un método en el componente PhpBB3 que actualizara la celda. Para crear dicho método podéis consultar la documentación de phpBB3:
Ahora sólo os faltaría modificar vuestra plantilla de phpBB para enlazar hacia vuestra aplicación Cake, con el registro de usuarios, el login y la edición de datos de vuestro perfil.
Podéis enlazar a ella o, en el caso de la edición de datos del perfil, tratar de modificar los formularios de phpBB para que envíen a la aplicación Cake y luego hacer algo como en el login con el referer para poder redirigir al usuario de nuevo al foro.
Esto lo dejo en vuestras manos dado que la solución es bastante abierta y ya llevo mucho rato dándoos la tabarra. Además, me parece que con las pistas que he dado ya tenéis más que suficiente para empezar
Pues con esto creo que ya hemos terminado. Si veis cualquier fallo u os peta la aplicación por algún lado no dudéis en preguntar, comentar y despotricar sobre mí. Tened en cuenta que he modificado el código considerablemente (desde el propio Wordpress…) para tratar de simplificarlo y puede que me haya comido alguna línea o incluso hasta algún apartado entero (esperemos que no…).
En cuanto vea vuestros comentarios trataré de contestarlos
Voy a explicaros una forma sencilla reemplazar vuestras capas flashMessage por bonitos mensajes dinámicos creados con jQuery imitando el estilo de Growl.
Para los que no lo sepáis, Growl es un sistema de advertencias de Mac que nos unifica todas las advertencias (tanto de sistema como las del resto de aplicaciones) siguiendo el mismo estilo y mostrándose y ocultándose con efectos de entrada y salida.
Hay varios plugins de jQuery que imitan Growl. En este enlace podéis encontrar los que probablemente sean los más conocidos y utilizados. Para el ejemplo que voy a hacer me he valido de Gritter, que es el plugin que, a mi parecer, más se asemeja a Growl.
(las versiones que he puesto entre paréntesis son las que he utilizado yo para el tutorial)
Descargad todo lo necesario y guardad todo donde más os plazca. En mi ejemplo lo he organizado así:
jquery-1.3.2.min.js a la carpeta /app/webroot/js/
jquery.gritter.min.js a la carpeta /app/webroot/js/
gritter.png a la carpeta /app/webroot/img/
gritter-close-ie6.gif a la carpeta /app/webroot/img/
gritter-long.png a la carpeta /app/webroot/img/
jquery.gritter.css renombrado y movido a /app/webroot/css/gritter.css
Fijaros que he renombrado el fichero CSS. Además de renombrarlo habrá que modificar las rutas de las imágenes hacia la ruta que toque. Si estáis siguiendo el tutorial al pie de la letra debéis reemplazar ../images/ por ../img/ en vuestro CSS.
Dado que queremos utilizar este sistema de notificaciones tipo Growl como sistema de notificaciones principal de CakePHP tenemos que modificar nuestro layout de HTML principal para cargar los JavaScripts en la cabecera entre otras cosas:
Las líneas marcadas en azul son las que os interesan. El resto podéis ignorarlas perfectamente.
En la línea 9 he añadido las librerías jQuery y Gritter. Recordad que para poder utilizar el Helperde JavasCript desde cualquier parte deberéis haber añadido dicho Helper en vuestro AppController:
<?php // /app/app_controller.php
class AppController extends Controller {
var $helpers = array('Html','Javascript');
}
En la línea 10 creamos la variable de JavaScript “webroot” que nos servirá más adelante para las plantillas de gritter. Si habéis leído alguno de mis tutoriales sobre cómo subir múltiples imágenes con uploadify en CakePHP quizás ya hayáis añadido esta variable a vuestro layout.
En la línea 12 añadimos el fichero CSS de Gritter.
Y en la línea 20, si no la teníais ya en vuestro layout, debéis añadir el método $session->flash() que será el que mostrará las notificaciones de Gritter.
Ya solo nos queda una cosa por hacer: crear la plantilla para cada tipo de notificación.
Como no quiero alargar mucho el tutorial haré sólo un par de layouts, uno para los errores y otro para las notificaciones normales.
Como podéis ver he utilizado la variable webroot para poder acceder fácilmente a nuestro directorio de imágenes. También he activado la variable sticky de gritter para dejar la notificación mostrada en pantalla hasta que el usuario la cierre manualmente, ya que considero que los flashMessage deben comportarse así. Cuando trabajo con Ajax no utilizo la variable sticky.
En estos ejemplos he utilizado lo básico al configurar Growl. Id a su página si queréis informaros sobre los demás métodos y variables disponibles para configurarlo.
Pues con esto habríamos terminado. Ahora cada vez que queráis mostrar notificaciones simplemente tendréis que especificar la plantilla a utilizar como segundo parámetro del método $this->Session->setFlash():
// Notificación de error
$this->Session->setFlash("Esto es un mensaje de error", "flash_error");
// Notificación informativa
$this->Session->setFlash("Esto es un mensaje informativo", "flash_info");
Pues ya está, ya lo tenemos ^^
Si queréis podéis descargar los ficheros más importantes en el zip que he preparado del tutorial. También podéis ver el ejemplo que he hecho a medida que hacía esta pequeña guía.
¡Ayúdanos a mantener el servidor de underave! Cualquier donación es válida, desde 1 mísero €urillo hasta la cantidad que vosotros queráis :D (la donación es mediante PayPal)
Ajuda'ns a mantenir el servidor d'underave! Qualsevol donació és vàlida, des d'un insignificant €urillu fins la quantitat que vulgueu :D (la donació es fa mitjançant paypal)