Saltar al contenido
Marcos Peña
Menú
  • Acerca de…
Menú

Conector Symfony2 Simple Machines Forum

Publicada el agosto 14, 2015septiembre 13, 2020 por Marcos

Introducción

El objetivo de este tutorial es describir una solución para sincronizar la autenticación de symfony2 (sf2) con los foros de Simple Machines Forums (SMF). A día de hoy no hay ningún bundle de foros para symfony listo para producción. CCDNForumForumBundle es lo mejor que he encontrado pero lamentable su desarrollador no lo mantiene ya. SMF ofrece una serie de ventajas que lo convierte en una solución atractiva:
  • Comunidad activa
  • Actualizaciones frecuentes y fáciles de aplicar
  • Modulable
  • Customizable
  • Gratis y de código libre (licencia BSD)
  • Soporta más de 40 idiomas
  • Interfaz atractiva e intuitiva
  • Gran variedad de funcionalidades para usuarios, moderadores y administradores
Pero no todo son ventajas. Su código está un tanto desorganizado y utiliza una base de datos sin integridad referencial. Aun así, sus desarrolladores se han preocupado por ofrecer soluciones programáticas para integrar y extender sus funcionalidades sin tener que modificar directamente los ficheros. Lo cual se agradece.
Te puede interesar este tutorial si… Utilizas sf2 con el bundle FOSUserBundle para gestionar tu base de usuarios y, además, tu organización requiere de un foro para éstos.
No te va interesar este tutorial si… Pretendes gestionar los usuarios desde SMF. Mi solución deshabilita la posibilidad de registrar usuarios desde el foro, o que éstos puedan modificar sus datos personales. Los datos han de ser actualizados desde sf2 y, a continuación, propagados programáticamente a SMF.
No entra en este tutorial la instalación y configuración de symfony y fosuserbundle. Se da por hecho de que existe una aplicación basada en sf2 con su tabla de usuarios. Tampoco se explicará la configuración de apache, php o mysql. O la instalación de módulos o paquetes de idiomas en SMF. De ahí que utilizaremos los nombres en inglés del apartado de administración de SMF.

Prerequisitos

Para llevar a cabo esta solución se han utilizado los siguientes componentes:
  • PHP, 5.6.9
  • MySql, 5.5.41
  • Apache, 2.4
  • Symfony, 2.5.12
  • FOSUserBundle, 1.3.6
  • SMF, 2.0.10
  • El paquete curl-easy, 1.1.4
Probablemente funcione con versiones menores, pero no lo he probado. Además, habremos configurado dos virtual hosts en apache, donde hospedaremos la aplicación de symfony y la de los foros:
  • Sf2: acme.com
  • SMF: foros.acme.com

Instalación, configuración y adaptación de SMF

Si tienes instalado y funcionando symfony2, doy por hecho que el despliegue de SMF es pan comido. Basta con descargarlo de la web oficial, descomprimirlo y desplegarlo donde apunte su correspondiente virtual host. En la dirección http://foros.acme.com/install.php, nos indicaran los pasos a seguir para su configuración incial. Una vez instalado, nos autenticamos con las credenciales del usuario administrador para aplicar los siguientes cambios de configuración:
  • Deshabilitar el registro de nuevos usuarios en SMF: Admin » Features and options » Members » Registration » Settings » Method of registration employed for new members: Registration disabled
  • Deshabilitamos el quick login: Admin » Features and Options » Layout » Show a quick login on every page
  • Habilitamos la posibilidad de compartir cookies entres subdominos: Admin » Features and Options » Configuration » Server Settings » Cookies and Sessions » Use subdomain independent cookies
  • Deshabilitamos la posibilidad de editar el perfil en SMF: Admin » Features and Options » Members » Regular Members » Mofify » Permissions » General Permissions » Personalize their profiles » Edit their account settings

Hooks para SMF

SMF ofrece a los desarrolladores la posibilidad de extender el comportamiento de los foros para integrarlos con aplicaciones externas. Gracias a una serie de anzuelos (hook en inglés) podemos enganchar bloques de código externo e interferir en acciones como la autenticación. O modificar el comportamiento de algunos de los botones. La lista de hooks disponibles es extensa, pero para nuestra solución sólo necesitaremos cuatro.
  1. integrate_pre_include con el que definiremos la ubicación del archivo con nuestros métodos adicionales.
  2. integrate_actions con el que modificamos algunas de las acciones de los foros.
  3. integrate_verify_user con el que verificaremos el usuario actual
  4. integrate_menu_buttons con el que podremos modificar el comportamiento de los botones del menú
Para dar de alta nuestras acciones personalizadas, crearemos el fichero add_action_hooks en la raíz de la aplicación de los foros, al mismo nivel que index.php
/add_action_hooks.php
PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
// If SSI.php is in the same place as this file, and SMF isn't defined, this is being run standalone.
if (file_exists(dirname(__FILE__) . '/SSI.php') && !defined('SMF'))
require_once(dirname(__FILE__) . '/SSI.php');
// Hmm... no SSI.php and no SMF?
elseif (!defined('SMF'))
die('<b>Error:</b> Cannot install - please verify you put this in the same place as SMF\'s index.php.');
 
add_integration_function('integrate_pre_include', '$sourcedir/Subs-SF2Connect.php');
add_integration_function('integrate_actions', 'tweak_user_buttons');
add_integration_function('integrate_verify_user', 'verify_sf2_user');
add_integration_function('integrate_menu_buttons', 'remove_menu_button');
?>
E invocamos desde http://foros.acme.com/add_action_hooks.php. A continuación lo borramos, ya no lo vamos a necesitar. Lo que sí que vamos a necesitar es el fichero con los métodos enganchados, aquél que definimos con la clave integrate_pre_include:
/Sources/ubs-SF2Connect.php
PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
<?php
if (!defined('SMF'))
die('Hacking attempt...');
 
/*
* Configuración
*/    
const SYMFONY_HOST_URL = 'http://acme.com';
const SYMFONY_CURRENT_USER_CONTEXT = '/user/current';
const SYMFONY_LOGIN_CONTEXT = '/user/login';
const SMF_COOKIE_LENGTH = 3600;
/*
* Fin configuración
*/    
 
/**
* Sobreescribe el comportamiento de los botones de
* login y logout con el definido en sf2_login() y smf_logout.
* Deshabilita las acciones asociadas al login rápido (login2)
* y de registro.
**/    
function tweak_user_buttons(&$actionArray)
{
    $actionArray['login'] = array('Subs-SF2Connect.php', 'sf2_login');
    $actionArray['logout'] = array('Subs-SF2Connect.php', 'smf_logout');
    unset($actionArray['login2']);
    unset($actionArray['register']);
}
/**
* Método principal del conector, se lanza cada vez que se carga la página.
* Si el usuario está autenticado en SMF, no hace nada más.
* En caso contrario, preguntará si lo está en symfony. Si afirmativo,
* queda autenticado. En caso contrario, queda marcado como invitado.
*
**/
function verify_sf2_user()
{        
 
      global $cookiename, $db_name, $db_prefix, $db_user, $db_passwd,
      $db_server, $sourcedir;
      $isAuthenticatedInSmf = isset($_COOKIE[$cookiename]);
 
      if(!$isAuthenticatedInSmf && !$_SESSION['user_is_guest']) { //check if user is authenticated in SF2
        $url = SYMFONY_HOST_URL . SYMFONY_CURRENT_USER_CONTEXT;
        $curlResource = curl_init();
        curl_setopt($curlResource, CURLOPT_URL, $url);
        curl_setopt($curlResource, CURLOPT_HTTPGET, true);
        curl_setopt($curlResource, CURLOPT_RETURNTRANSFER, true);  // Get returned value as string (don"t put to screen)
        curl_setopt($curlResource, CURLOPT_HTTPHEADER, array(
            'Content-Type: application/json',
            'Accept: application/json'
        ));
        // Retrieving session ID
        $symfonyCookie = $_COOKIE['SFSESSID'];
        if($symfonyCookie === null) {
            $_SESSION['user_is_guest'] = true;
 
            return;
        }
        $strCookie = 'SFSESSID=' . $symfonyCookie . '; path=/';        
        curl_setopt( $curlResource, CURLOPT_COOKIE, $strCookie );
        $result = curl_exec($curlResource);
        curl_close($curlResource);  
        $jsonResponse = json_decode($result);
        $isAuthenticatedInSF2 = $jsonResponse->authenticated;
 
        if(!$isAuthenticatedInSF2) {  
        
            return;
        }
        $conn = new mysqli($db_server, $db_user, $db_passwd);
        // Check connection
        if ($conn->connect_error) {
            die('Connection failed: ' . $conn->connect_error);
        }
        $sf2UserEmail = $jsonResponse->email;
 
        if($sf2UserEmail === null) {
            die('Email not found');
        }
        $query = 'SELECT id_member, passwd, password_salt FROM ' . $db_name . '.smf_members WHERE email_address = \'' . $sf2UserEmail . '\'';
        $result = mysqli_query($conn, $query);
        $row   = mysqli_fetch_row($result);
        if($row === null) {
            die('User not found in smf');
        }
        $smfMemberId = $row[0];        
        $password = $row[1];        
        $salt = $row[2];        
        $conn->close();
        
        //Set cookie:
        require_once($sourcedir . '/Subs-Auth.php');
        setLoginCookie(SMF_COOKIE_LENGTH, $smfMemberId, sha1($password . $salt));
        $_SESSION['user_is_guest'] = false;
        
        return intval($smfMemberId);
        
      } else {
            $_SESSION['user_is_guest'] = false;      
      }
}
/**
* Quita de la plantilla los botones que no queremos.
*
**/
function remove_menu_button(&$menu_buttons)
{
    if (isset($menu_buttons['logout'])) {
        unset($menu_buttons['logout']);
        unset($menu_buttons['register']);
    }
    
}
/**
* Manda a la página de login de symfony.
*
**/
function sf2_login()
{
    $symfonyLoginPage = SYMFONY_HOST_URL . SYMFONY_LOGIN_CONTEXT;
    $redirect = $symfonyLoginPage . '?referer=' . 'http://$_SERVER[HTTP_HOST]';
    header('Location: ' . $redirect);
    die();
}
/**
* Desconecta al usuario de smf.
*
**/
function smf_logout()
{
    require_once($sourcedir . '/LoginOut.php');
    Logout(true, false);
    
    $response = array('sucess' => true, 'message' => 'user has been logout');
    header('Content-Type: application/json');
    echo json_encode($response);
    
    die();
}
?>
Para que el invento funcione es importante que el nombre de la cookie de SMF sea el que viene por defecto, SMFCookie956. Se puede comprobar en /Settings.php
Bueno, ya tenemos listos los foros. Siguiente paso.

Adaptaciones en Symfony2

Configuración

Lo primero, tenemos que configurar las cookies de symfony.
src/app/config/config.yml
YAML
1
2
3
4
framework:
    session:
        cookie_domain: .acme.com
        name: SFSESSID
Esto es importante para identificar correctamente la cookie de cada aplicación. Es necesario que ambas aplicaciones puedan reconocerse a través de sus cookies, a pesar de estar en subdominios diferentes. Por supuesto, ambas aplicaciones han de compartir mismo dominio. Oh, y vamos a necesitar una serie de parámetros configurables, cuestión de no meterlos a pelo en el código.
src/app/config/config.yml
YAML
1
2
3
4
5
acme_user:
    smf_bridge:
        enabled: true
        smf_logout_url: "http://foros.acme.com/index.php?action=logout"
        smf_cookie_name: "SMFCookie956"
src/ACME/UserBundle/DependencyInjection/Configuration.php
PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<?php
 
namespace ACME\UserBundle\DependencyInjection;
 
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
 
/**
* This is the class that validates and merges configuration from your app/config files
*
* To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/extension.html#cookbook-bundles-extension-config-class}
*/
class Configuration implements ConfigurationInterface
{
    /**
     * {@inheritDoc}
     */
    public function getConfigTreeBuilder()
    {
        $treeBuilder = new TreeBuilder();
        $rootNode = $treeBuilder->root('acme_user');
 
        $rootNode
                ->children()
                    ->arrayNode('smf_bridge')
                    ->addDefaultsIfNotSet()
                        ->children()                            
                            ->booleanNode('enabled')->defaultTrue()->end()
                            ->scalarNode('smf_logout_url')->defaultValue('https://foros.acme.com')->cannotBeEmpty()->end()
                            ->scalarNode('smf_cookie_name')->defaultValue('SMFCookie956')->cannotBeEmpty()->end()
                        ->end()
                    ->end()
                ->end();
        
        return $treeBuilder;
    }
}
?>
src/ACME/UserBundle/DependencyInjection/ACMEUserExtension.php
PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?php
 
namespace ACME\UserBundle\DependencyInjection;
 
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader;
 
/**
* This is the class that loads and manages your bundle configuration
*
* To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/extension.html}
*/
class ACMEUserExtension extends Extension
{
    /**
     * {@inheritDoc}
     */
    public function load(array $configs, ContainerBuilder $container)
    {
        $configuration = new Configuration();
        $config = $this->processConfiguration($configuration, $configs);
 
        $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
        $loader->load('services.yml');
        
        $container->setParameter('acme.user.smf_bridge.enabled', $config['smf_bridge']['enabled']);                
        $container->setParameter('acme.user.smf_bridge.smf_logout_url', $config['smf_bridge']['smf_logout_url'])
        $container->setParameter('acme.user.smf_bridge.smf_cookie_name', $config['smf_bridge']['smf_cookie_name']);          
    }
}
?>

Lógica

SMF ha de saber si el usuario está identificado en symfony. Claro está. Rápido, necesitamos un controlador y una ruta.
app/config/routing.yml
YAML
1
2
3
acme_user_current:
    pattern: user/current
    defaults: {_controller: ACMEUserBundle:UserSecurity:currentUser}
src/ACME/UserBundle/Controller/UserSecurityController.php
PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<?php
 
namespace ACME\UserBundle\Controller;
 
use FOS\UserBundle\Controller\SecurityController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Security\Core\SecurityContext;
 
class UserSecurityController extends SecurityController
{
    /**
     * Returns current logged user data
     * Won't return anything if user is not logged in
     *
     * @return JsonResponse
     * @throws \LogicException
     */
    public function currentUserAction()
    {
        if (!$this->container->has('security.context')) {
            throw new \LogicException('The SecurityBundle is not registered in your application.');
        }
 
        $response = array('authenticated' => false);
 
        if (null === $token = $this->container->get('security.context')->getToken()) {
            return new JsonResponse($response);
        }
 
        if (!is_object($user = $token->getUser())) {
            return new JsonResponse($response);
        }
        $response = array(
            'authenticated' => true,
            'name' => $user->getFirstName(),
            'lastname' => $user->getLastname(),
            'dni' => $user->getDni(),
            'email' => $user->getEmail()
        );
 
        return new JsonResponse($response);
    }
 
}
?>
Si no lo tenéis ya hecho, hay que dar de alta un par handlers que se encargarán de gestionar el flujo de la aplicación después de un login o logout. O sea, desconectar al usuario de los foros cuando éste se desconecte de ACME. Y redirigirle de vuelta a los foros después de una autenticación exitosa.
src/ACME/UserBundle/Resources/config/services.yml
YAML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
services:
    acme.authentication.handler.login_success_handler:
                class:    ACME\UserBundle\Security\Authentication\Handler\LoginSuccessHandler
                arguments:    [@router, @security.context]
                tags:
                        - { name: 'monolog.logger', channel: 'security' }            
 
    acme.authentication.handler.logout_success_handler:
                class:    ACME\UserBundle\Security\Authentication\Handler\LogoutSuccessHandler
                arguments:    [@router, @security.context, @session]
                tags:
                        - { name: 'monolog.logger', channel: 'security' }    
                calls:
                    - [ setParameters, [%acme.user.smf_bridge.smf_logout_url%, %acme.user.smf_bridge.smf_cookie_name%, %acme.user.smf_bridge.enabled%] ]                        
src/ACME/UserBundle/Security/Authentication/Handler/LoginSuccessHandler
PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<?php
 
namespace ACME\UserBundle\Security\Authentication\Handler;
 
use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\SecurityContext;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Cmf\Component\Routing\ChainRouter as Router;
 
class LoginSuccessHandler implements AuthenticationSuccessHandlerInterface
{
 
    protected $router;
    protected $securityContext;
    protected $adminFirewallName = 'admin';
 
    public function __construct(Router $router, SecurityContext $securityContext)
    {
        $this->router = $router;
        $this->securityContext = $securityContext;
    }
 
    public function onAuthenticationSuccess(Request $request, TokenInterface $token)
    {
        $firewallName = $this->securityContext->getToken()->getProviderKey();
        $redirectRoute = $firewallName === $this->adminFirewallName ? 'sonata_admin_dashboard' : 'homepage';
        $referer = $request->server->get('HTTP_REFERER');
        $needle = 'referer=';
        //Do we have to redirect?
        if (($pos =  strpos($referer, $needle)) !== false) {
            $referer = urldecode($referer);
            $redirectTo = substr ($referer, $pos + strlen($needle));
 
            return new RedirectResponse($redirectTo);
        }        
 
        return new RedirectResponse($this->router->generate($redirectRoute));
    }
 
}
?>
src/ACME/UserBundle/Security/Authentication/Handler/LogoutSuccessHandler
PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
<?php
 
namespace ACME\UserBundle\Security\Authentication\Handler;
 
use Symfony\Cmf\Component\Routing\ChainRouter as Router;
use Symfony\Component\Security\Http\Logout\LogoutSuccessHandlerInterface;
use Symfony\Component\Security\Core\SecurityContext;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Session\Session;
 
class LogoutSuccessHandler implements LogoutSuccessHandlerInterface
{
 
    protected $router;
    protected $securityContext;
    protected $session;
    protected $adminFirewallName = 'admin';
    protected $smfUrl;
    protected $smfCookieName;
    protected $smfEnabled = false;
 
    public function __construct(Router $router, SecurityContext $securityContext, Session $session)
    {
        $this->router = $router;
        $this->securityContext = $securityContext;
        $this->session = $session;
    }
 
    public function onLogoutSuccess(Request $request)
    {
        $uri = $request->getUri();
        $adminFirewall = false;
        if (strpos($uri, '/admin') !== false) {
            $adminFirewall = true;
        }
        $redirectRoute = $adminFirewall ? 'sonata_user_admin_security_login' : 'acme_user_login';
        $this->securityContext->setToken(null);
        $request->getSession()->invalidate(1);
        if ($this->smfEnabled) {
            $cookies = $request->cookies;
            foreach ($cookies as $key => $value) {
                if ($key === $this->smfCookieName) { //usuario autenticado en smf
                    $curlRequest = new \cURL\Request($this->smfUrl);
                    $curlRequest->getOptions()
                            ->set(CURLOPT_TIMEOUT, 5)
                            ->set(CURLOPT_RETURNTRANSFER, true)
                            ->set(CURLOPT_COOKIE, $this->smfCookieName . '=' . urlencode($value));
                    $response = $curlRequest->send();
                    //ignora la respuesta json: en el peor de los casos el usuario se quedará logado en smf
                    $feed = json_decode($response->getContent(), true);
                }
            }
        }
        
        return new RedirectResponse($this->router->generate($redirectRoute));
    }
 
    public function setParameters($smfUrl, $smfCookieName, $smfEnabled)
    {
        $this->smfUrl = $smfUrl;
        $this->smfCookieName = $smfCookieName;
        $this->smfEnabled = $smfEnabled;
    }
}
?>
Nótese la utilización del paquete cURL. Para incluirlo (curl-easy) hay que darlo de alta en composer.json y adaptar la clave «autoload» para que se pueda acceder por namespace
./composer.json
JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
...
    "require": {
        "php": ">=5.3.3",
        "symfony/symfony": "2.5.*",
        "stil/curl-easy": "*"
...  
        },
...
    "autoload": {
        "psr-0": {
            "": "src/"
        },
        "classmap": ["src/"]
    },
...    
Y, con esto, ya tenemos conectados symfony y SMF. Ya sólo nos queda «sincronizar» los datos. Cuando un usuario es dado de alto en smf2, hay que darlo de alta en SMF. O, si no, poco vamos a conectar… Y, por supuesto, debemos actualizar sus datos personales del foro (sobre todo el email) cuando se actualicen en ACME.

Replicación de datos Symfony -> SMF

Hay varias maneras de hacerlo. Una de ella es con triggers en la base de datos. A modo de ejemplo:
PgSQL
1
2
DELIMITER $$
    CREATE TRIGGER [crayon-628c1459b019c957336965 inline="true" ]update_smf
AFTER UPDATE ON fos_user_user FOR EACH ROW BEGIN IF NEW.email <> OLD.email AND new.enabled = 1 THEN IF (SELECT COUNT(*) FROM smf.smf_members WHERE email_address = new.email) = 0 THEN INSERT INTO smf.smf_members SET email_address = NEW.email, is_activated = 1, date_registered = CURDATE(), real_name = NEW.firstname; ELSE UPDATE smf.smf_members SET smf.smf_members.email_address = NEW.email WHERE smf.smf_members.email_address = NEW.socio_id; END IF; END IF; IF NEW.enabled <> OLD.enabled THEN UPDATE smf.smf_members, acme.fos_user_user SET smf.smf_members.disabled = CASE WHEN NEW.enabled = 1 THEN 0 ELSE 1 END WHERE smf.smf_membersr.email_address = NEW.email; END IF; END$$ DELIMITER ; [/crayon] ¿Que lo queremos gestionar desde el propio symfony? Entonces tendríamos que engancharnos a algún EventListener. Por ejemplo, si utilizais SonataAdminBundle, en la clase de admin del usuario:
src/ACME/UserBundle/Admin/Entity/UserAdmin.php
PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?php
namespace AUGC\UserBundle\Admin\Entity;
use Sonata\UserBundle\Admin\Entity\UserAdmin as BaseUserAdmin;
...
class UserAdmin extends BaseUserAdmin
{
...
    /**
     * Hook on pre-update operations
     */
    public function preUpdate($user)
    {
     ...
        $entityManager = $container->get('doctrine.orm.entity_manager');
        $dateOfbirth = $user->getDateOfBirth() ? : new \DateTime();
        $formattedDateOfBirth = $dateOfbirth->format('Y-m-d G:i:s');
        $smfUserQuery = "REPLACE INTO smf.smf_members "
                . "(real_name, member_name, passwd, email_address, is_activated, date_registered, member_ip, birthdate, id_post_group, pm_ignore_list, signature, message_labels, buddy_list, ignore_boards, openid_uri)"
                . " VALUES ('" . $user->getFirstname() . " " . $user->getLastName() . "', '" . $user->getDni() . "', '" . $smfPassword . "', '" . $user->getEmail() . "', '1', '" . time() . "', '" . $container->get('request')->getClientIp() . "', '" . $formattedDateOfBirth . "', '4', '', '', '', '', '', '');";    
        try {
            $entityManager->getConnection()->executeQuery($smfUserQuery);
            $entityManager->getConnection()->commit();
        } catch (\Exception $exception) {
            $entityManager->getConnection()->rollBack();
            $entityManager->getConnection()->close();
            throw $exception;
        }    
}
?>
Listos.

Deja una respuesta Cancelar la respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Artículos

  • abril 2022
  • marzo 2022
  • febrero 2022
  • enero 2022
  • diciembre 2021
  • noviembre 2021
  • octubre 2021
  • septiembre 2021
  • agosto 2021
  • julio 2021
  • junio 2021
  • mayo 2021
  • abril 2021
  • marzo 2021
  • febrero 2021
  • enero 2021
  • diciembre 2020
  • noviembre 2020
  • octubre 2020
  • septiembre 2020
  • agosto 2020
  • julio 2020
  • junio 2020
  • mayo 2020
  • abril 2020
  • marzo 2020
  • febrero 2020
  • enero 2020
  • diciembre 2019
  • noviembre 2019
  • octubre 2019
  • septiembre 2019
  • agosto 2019
  • julio 2019
  • junio 2019
  • mayo 2019
  • abril 2019
  • marzo 2019
  • febrero 2019
  • enero 2019
  • noviembre 2018
  • octubre 2018
  • julio 2016
  • mayo 2016
  • abril 2016
  • agosto 2015
  • febrero 2014
  • junio 2013
  • febrero 2013
  • noviembre 2012
  • octubre 2012
  • septiembre 2012
  • abril 2012
  • enero 2012

Categorías

  • Cómo
  • Consejos
  • Hacks
  • Sin sentido
  • Tutorial

Etiquetas

apache API aws cache Centos Codeigniter EAC firefox flash freebsd freenas gmail Google guacamole java Javascript jQuery kubuntu Linux mysql nas4free nginx php plesk postfix python react rtorrent Seguridad sftp sonata-admin spring spring-boot sql ssh ssl symfony symfony2 symfony4 thymeleaf ubuntu vnc wget windows wine

Entradas recientes

  • Confirmación antes de apagar
  • cURL error 60: SSL certificate problem: unable to get local issuer certificate
  • Oracle JDK 7 is NOT installed.
  • ssh: connect to host: Connection refused
  • Restringir acceso a un bucket de AWS s3 por IP

Comentarios recientes

  • Marcos en Deobbing flash
  • Irosales en Deobbing flash
  • Instalación certificado SSL de un vendedor – Marcos Peña en Instala un certificado SSL en tres pasos
  • gerMdz en Cómo instalar un certificado letsencrypt para un sitio gestionado por Cloudflare
  • Jose Olalla en Cómo ampliar el espacio en disco en 1and1

Archivos

  • abril 2022
  • marzo 2022
  • febrero 2022
  • enero 2022
  • diciembre 2021
  • noviembre 2021
  • octubre 2021
  • septiembre 2021
  • agosto 2021
  • julio 2021
  • junio 2021
  • mayo 2021
  • abril 2021
  • marzo 2021
  • febrero 2021
  • enero 2021
  • diciembre 2020
  • noviembre 2020
  • octubre 2020
  • septiembre 2020
  • agosto 2020
  • julio 2020
  • junio 2020
  • mayo 2020
  • abril 2020
  • marzo 2020
  • febrero 2020
  • enero 2020
  • diciembre 2019
  • noviembre 2019
  • octubre 2019
  • septiembre 2019
  • agosto 2019
  • julio 2019
  • junio 2019
  • mayo 2019
  • abril 2019
  • marzo 2019
  • febrero 2019
  • enero 2019
  • noviembre 2018
  • octubre 2018
  • julio 2016
  • mayo 2016
  • abril 2016
  • agosto 2015
  • febrero 2014
  • junio 2013
  • febrero 2013
  • noviembre 2012
  • octubre 2012
  • septiembre 2012
  • abril 2012
  • enero 2012

Categorías

  • Cómo
  • Consejos
  • Hacks
  • Sin sentido
  • Tutorial

Meta

  • Acceder
  • Feed de entradas
  • Feed de comentarios
  • WordPress.org
© 2022 Marcos Peña | Funciona con Minimalist Blog Tema para WordPress