SMFPortal.de

Willkommen !

Autor Thema: Joomla (<)=> SMF 2.0  (Gelesen 3214 mal)

0 Mitglieder und 1 Gast betrachten dieses Thema.

bembelimen

  • Mitglied
  • **
  • Beiträge: 27
Joomla (<)=> SMF 2.0
« am: 04. Oktober 2010, 09:15:42 »
Hallo,

ich weiß zwar, dass es schon einige Lösungen für eine Bridge zwischen Joomla und SMF 2.0 gibt, aber da ich diese meistens sehr überladen empfinde, habe ich mich vor einiger Zeit mal dran gesetzt und mir selbst was zusammen gebastelt. Im Gegensatz zu meinem alten Vorschlag wird dieses mal keine 3rd Party Komponente in Joomla genutzt (Community Builder), sondern rein auf Coremittel zugegriffen. Wie auch letztes mal starte ich zuerst mit einer Warnung:



WICHTIG:

  • Ich werde nur eine Code-Skizze verfassen, kein fertiges Plugin anhängen. Da dieser Weg meines Erachtens nicht ganz einfach ist, ist er nur was für Leute, die wissen, was passiert. ALSO BITTE NICHT KOPIEREN SONDERN SELBST DENKEN!!
  • Das Teil ist ein bisschen ein "Proof of concept", also auch hier bezweifle ich, dass es perfekt ist. Ich bin also für jegliche Verbesserungsvorschläge dankbar.
  • Dieses mal sollte der synchronisierte Login funktionieren



VORRAUSSETZUNGEN:




VORGEHENSWEISE:

  • Es wird ein ganz normales Joomla! User Plugin erstellt, dass auf die verschiedensten Login/Registrierungstrigger reagiert



DIE XML DATEI:

Ok, nun zum Plugin, zuerst die XML, eigentlich nichts besonderes, 3 Parameter und das wars auch schon. Name: jsmfbridge.xml

Code
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE install PUBLIC "-//Joomla! 1.5//DTD plugin 1.0//EN" "http://dev.joomla.org/xml/1.5/plugin-install.dtd">
<install version="1.5" type="plugin" group="user">
    <name>Joomla to SMF Bridge</name>
    <author>Benjamin Trenkle</author>
    <creationDate>April 2010</creationDate>
    <copyright>Copyright (C) 2010 - Benjamin Trenkle</copyright>
    <license>GNU General Public License, http://www.gnu.org/licenses/gpl-2.0.html</license>
    <authorEmail></authorEmail>
    <authorUrl></authorUrl>
    <version>1.0</version>
    <description>An joomla to SMF user synchronisation plugin</description>
    <files>
        <filename plugin="jsmfbridge">jsmfbridge.php</filename>
    </files>
    <params>
        <param name="smfprefix" type="text" default="smf_" label="SMF Prefix" description="The SMF database suffix" />
        <param name="path" type="text" default="forum" label="SMF Path" description="The path to the smf directory" />
        <param name="domain" type="text" default="" label="Domain" description="The global domain for the cookie" />
    </params>
</install>

Der erste Parameter "SMF Prefix" sollte selbsterklärend sein, einfach das SMF Datenbankpräfix
Der zweite Parameter sollte den relativen(!!) Pfad zum Forum enthalten, ausgehend von dem Hauptordner der Joomla!-Installation
Der dritte Parameter ist die Domain für das Cookie, z.B. ".domainname.tld"



DAS PLUGIN ITSELF:

Nun kommt die PHP Datei. Name: jsmfbridge.php

Zu aller erst ein paar Überlegungen:

Das SMF hat eine "Blacklist" für Benutzernamen, dies hat Joomla! nicht. Also muss vor dem Speichern der Registrierung diese abgefragt werden und ggf. darauf reagiert werden. Zusätzlich habe ich festgestellt, dass man nicht erkennen kann, ob ein Benutzer seinen Status wechselt (Benutzer blocken/freischalten etc.), somit muss vor dem speichern der alte Status temporär vermerkt werden.

So, die Grundstruktur des Plugins sieht nun folgendermaßen aus:

Code
<?php
/**
 * @version     $Id$
 * @package     Joomla
 * @subpackage  Joomla!SMFBridge
 * @copyright   (C) 2010 Benjamin Trenkle
 * @license     GNU/GPL, see http://www.gnu.org/copyleft/gpl.html and LICENSE.php
 *
 * Joomla!SMFBridge - A Joomla 1.5.x <=> SMF 2.0 Bridge
 */

###############################################################################
###############################################################################

// Ensure, that the file was included by Joomla!

defined( '_JEXEC' ) or jexit();

###############################################################################
###############################################################################

// load the JPlugin Class
jimport( 'joomla.plugin.plugin' );

class plgUserJSMFBridge extends JPlugin {

    // private valiables for conserving old values
    private $_username = null;
    private $_email = null;
    private $_block = null;
    private $_activation = null;

    // constructor
    public function __construct($subject, $config) {

        parent::__construct($subject, $config);

    }

    // will be called before saving a user
    public function onBeforeStoreUser($user, $isnew) {

        
    }

    // will be called after saving a user
    public function onAfterStoreUser($user, $isnew, $success, $msg) {


    }

    // will be called when the user try to login
    public function onLoginUser($response, $options) {

        
    }

    // will be called, when the login failes
    public function onLoginFailure($response) {


    }

    // will be called on logout
    public function onLogoutUser($parameters, $options) {


    }

}

?>
« Letzte Änderung: 04. Oktober 2010, 11:02:48 von bembelimen »

bembelimen

  • Mitglied
  • **
  • Beiträge: 27
Re: Joomla (<)=> SMF 2.0
« Antwort #1 am: 04. Oktober 2010, 09:16:05 »
DAS SPEICHERN (REGISTRIERUNG/PROFIL ÄNDERN):

hier werden nun die Trigger für das speichern eines Benutzers erklärt. Dies betrifft im Insbesonderen "onBeforeStoreUser" und "onAfterStoreUser".

Code
    public function onBeforeStoreUser($user, $isnew) {

        // so wir haben keinen neuen Benutzer, also heißt das, dass das Profil verändert wurde.
        // Wir merken uns alle Felder, die evtl. Veränderbar sind um die Daten später abgleichen zu können.
        if(!$isnew) {

            $this->_username = $user['username'];
            $this->_email = $user['email'];
            $this->_block = $user['block'];
            $this->_activation = $user['activation'];

            return true;

        }

        // wenn wir hier landen, dann wird versucht ein neuer Benutzer anzulegen
        // Also wollen wir testen, ob der username nicht auf der schwarzen SMF Liste steht
        // zuerst initialisieren wir das Datenbankobjekt und den Mainframe
        $db = JFactory::getDBO();
        $app = JFactory::getApplication();
        
        $this->params->def('smfprefix', 'smf_');

        // hier holen wir uns den Username aus den Requestvariablen
        $username = JRequest::getVar('username', '', 'post', 'username');

        // wir laden uns alle verbotenen Usernames
        $query = '
            SELECT
                '.$db->nameQuote('value').'
            FROM
                '.$db->nameQuote($this->params->get('smfprefix').'settings').'
            WHERE
                '.$db->nameQuote('variable').'='.$db->Quote('reserveNames')
        ;

        $db->setQuery($query);

        $names = $db->loadResult();

        $names = explode("\n", $names);

        $names = array_map('strtolower', $names);

        $cid = JRequest::getVar('cid', array());

        $url = '';

        // nun testen wir, ob der Username nicht erlaubt ist
        if(in_array(strtolower($username), $names)) {

            // falls verboten, wird zurück zum Formular geleitet
            switch($app->getClientId()){

                case 0:
                    $url = JRoute::_('index.php?option=com_user&view=register');
                    break;

                case 1:
                    $url = 'index.php?option=com_users&view=user&layout=form';
                    $url .= (isset($cid[0]) && (int)$cid[0]>0)? '&cid[]='.(int)$cid[0] : '';
                    break;

            }

            $app->redirect($url , 'Cannot use '.$username.' as Username', 'warning');
            return false;

        }

        // ansonsten ist alles ok
        return true;
        
    }

Eigentlich passiert hier nichts ungewöhnliches, beim Neuanlegen wird de Blackliste vom SMF überprüft, beim Editieren werden einfach nur die wichtigen Werte Zwischengespeichert.

Nun kommt der spannende Teil: Der Benutzer wird gespeichert.

Code
    public function onAfterStoreUser($user, $isnew, $success, $msg) {

        // wenn Joomla! den Benutzer nicht speichern konnte, dann wollen wir das auch nicht
        // also raus hier
        if(!$success) {

            return true;

        }

        // Initialisierung der bekannten Objekte
        $db = JFactory::getDBO();
        $date = JFactory::getDate();

        // set database prefix
        $this->params->def('smfprefix', 'smf_');

        // ok, hier haben wir keinen neuen Benutzer, das Profil wird also ganz normal aktualisiert
        if(!$isnew) {
           
            // wir basteln uns nach und nach das Datenbankquery für die Aktualisierung
            $query = '
                UPDATE
                    '.$db->nameQuote($this->params->get('smfprefix').'members').'
                SET';

            $values = array();

            // wenn der Username aktualisiert wurde, dann tun wir das auch im SMF
            if($this->_username != $user['username']) {

                $values[] = $db->nameQuote('member_name').'='.$db->Quote($user['username']);
                $values[] = $db->nameQuote('real_name').'='.$db->Quote($user['username']);

            }
            // wenn die Email sich geändert hat => SMF will es wissen
            if($this->_email != $user['email']) {

                $values[] = $db->nameQuote('email_address').'='.$db->Quote($user['email']);

            }
            // haben wir ein neues Passwort? Ok, dann ändern wir das auch
            if(strlen($user['password_clear'])) {

                $values[] = $db->nameQuote('passwd').'='.$db->Quote(sha1(strtolower($user['username']).$user['password_clear']));

            }

            // wenn der Benutzer nicht geblockt ist, dann brauchts im SMF auch keinen Validierungscode
            if(!$user['block']) {

                $values[] = $db->nameQuote('validation_code').'='.$db->Quote('');

            }

            // und aktiviert ist der Benutzer automatisch, wenn er nicht geblockt ist
            $values[] = $db->nameQuote('is_activated').'='.$db->Quote((int)!$user['block']);

            // das ganze wird in einen String umgewandelt, an das Query angehängt und abgesendet
            $query .= implode(', ', $values);
            $query .= '
                WHERE
                    '.$db->nameQuote('member_name').'='.$db->Quote($this->_username);

            $db->setQuery($query);

            $db->query();

            // hier wird es ein bisschen knifflig:
            // wenn der Benutzer vorhin noch nicht freigeschalten war, aber jetzt durch den Aktivierungscode freigeschalten wird
            // dann ist er ein neuer Benutzer und wir müssen die Statistiken aktualisieren.
            if($this->_block && !$user['block'] && strlen($this->_activation) && !strlen($user['activation'])) {

                // relevante Informationen auslesen
                $query = '
                    SELECT
                        COUNT(*) as '.$db->nameQuote('totalMembers').',
                        MAX(id_member) as '.$db->nameQuote('latestMember').'
                    FROM
                        '.$db->nameQuote($this->params->get('smfprefix').'members').'
                    WHERE
                        '.$db->nameQuote('is_activated').' = 1'
                ;

                $db->setQuery($query);

                $totalMembers = 0;
                $latestMember = 0;

                list($totalMembers, $latestMember) = $db->loadRow();

                $settings = array();

                // Werte neu setzen
                $settings['totalMembers'] = $totalMembers;
                $settings['latestMembers'] = $latestMember;
                $settings['latestRealName'] = $user['username'];
                $settings['memberlist_updated'] = time();

                // alle Werte aktualisieren
                foreach ($settings as $key => $value) {

                    $query = '
                        UPDATE
                            '.$db->nameQuote($this->params->get('smfprefix').'settings').'
                        SET
                            '.$db->nameQuote('value').'='.$db->Quote($value).'
                        WHERE
                            '.$db->nameQuote('variable').'='.$db->Quote($key)
                    ;

                    $db->setQuery($query);
                    $db->query();

                }

            }

            return true;

        }

        // landen wir hier, haben wir einen neuen Benutzer, der sich frisch registriert hat
        // zuerst wollen wir die Standardusergruppe auslesen
        $query = '
            SELECT '.
                $db->nameQuote('id_group').'
            FROM '.
                $db->nameQuote($this->params->get('smfprefix').'membergroups').'
            WHERE '.
                $db->nameQuote('min_posts').' = 0';
       
        $db->setQuery($query);
        $groupid = $db->loadResult();
        $groupid = !is_null($groupid)? $groupid : 0;

        // load ip
        $ip = JRequest::getCmd('REMOTE_ADDR', '', 'server');

        // nun werden alle Informationen des Benutzers in die SMF Datenbanktabelle gespeichert
        $query = '
            INSERT INTO
                '.$db->nameQuote($this->params->get('smfprefix').'members').'
            (
                '.$db->nameQuote('member_name').',
                '.$db->nameQuote('date_registered').',
                '.$db->nameQuote('real_name').',
                '.$db->nameQuote('passwd').',
                '.$db->nameQuote('email_address').',
                '.$db->nameQuote('pm_email_notify').',
                '.$db->nameQuote('member_ip').',
                '.$db->nameQuote('member_ip2').',
                '.$db->nameQuote('is_activated').',
                '.$db->nameQuote('validation_code').',
                '.$db->nameQuote('id_post_group').',
                '.$db->nameQuote('password_salt').'
            )
            VALUES
            (
                '.$db->Quote($user['username']).',
                '.$date->toUnix().',
                '.$db->Quote($user['username']).',
                '.$db->Quote(sha1(strtolower($user['username']).$user['password_clear'])).',
                '.$db->Quote($user['email']).',
                '.$db->Quote(1).',
                '.$db->Quote($ip).',
                '.$db->Quote($ip).',
                '.$db->Quote((int)!$user['block']).',
                '.$db->Quote(substr(preg_replace('/\W/', '', md5(rand())), 0, 10)).',
                '.$db->Quote($groupid).',
                '.$db->Quote(substr(md5(rand()), 0, 4)).'
            )'
        ;

        $db->setQuery($query);
        $result = $db->query();

        // falls was schief gelaufen ist....
        if($result === false) {

            // geben wir eine Fehlermeldung aus
            JError::raiseWarning(500, 'Could not synchronize with SMF table');

            jimport('joomla.utilities.utility');

            $app = JFactory::getApplication();

            $from = $app->getCfg('mailfrom');
            $admin = JFactory::getUser(62);

            $subject = 'Could not synchronize User to SMF table';
            $body = $user['username'].' could not be synchronize to SMF table';

            // und schreiben eine Email an den Admin (evtl. ID anpassen, falls ein anderer User die Email bekommen soll)
            JUtility::sendMail($from, $admin->get('name'), $admin->get('email'), $subject, $body);

            return false;

        }

        return true;

    }
« Letzte Änderung: 04. Oktober 2010, 09:38:39 von bembelimen »

bembelimen

  • Mitglied
  • **
  • Beiträge: 27
Re: Joomla (<)=> SMF 2.0
« Antwort #2 am: 04. Oktober 2010, 09:16:19 »
LOGIN:

Zum Login muss ich ehrlich gestehen, dass ich mit mit den SMF Hooks noch nicht wirklich anfreunden konnte und auch ein paar Cookieprobleme hatte, wenn das Login über verschiedene Subdomains gehen sollte. Vielleicht hat ja jemand die Muse das sauber(er) zu lösen?

Code
    public function onLoginUser($response, $options) {

        $app = JFactory::getApplication();

        // Im Backend brauchen wir keine Syncronisation
        if($app->isAdmin()) {

            return true;

        }

        $db = JFactory::getDBO();

        // set database prefix
        $this->params->def('smfprefix', 'smf_');
        $this->params->def('path', 'forum');
        $this->params->def('domain');

        $passwd = sha1(strtolower($response['username']).$response['password']);

        // wir schauen, ob der Benutzer mit diesem Passwort existiert und ob er freigeschalten ist
        $query = '
            SELECT
                '.$db->nameQuote('id_member').',
                '.$db->nameQuote('password_salt').'
            FROM
                '.$db->nameQuote($this->params->get('smfprefix').'members').'
            WHERE
                '.$db->nameQuote('member_name').'='.$db->Quote($response['username']).'
            AND
                '.$db->nameQuote('email_address').'='.$db->Quote($response['email']).'
            AND
                '.$db->nameQuote('passwd').'='.$db->Quote($passwd).'
            AND
                '.$db->nameQuote('is_activated').'='.$db->Quote(1)
        ;

        $db->setQuery($query);

        $id = null;
        $salt = '';
        
        list($id, $salt) = $db->loadRow();

         // falls der user nicht existiert, brechen wir ab
        if(!isset($id) || is_null($id) || $id < 1) {

            return false;

        }

        // ansonsten laden wir die Cookieinfos
        $query = '
            SELECT
                '.$db->nameQuote('value').'
            FROM
                '.$db->nameQuote($this->params->get('smfprefix').'settings').'
            WHERE
                '.$db->nameQuote('variable').'='.$db->Quote('cookieTime')
        ;

        $db->setQuery($query);

        $cookieTime = $db->loadResult();

        jimport('joomla.filesystem.file');

        // ...laden den Cookienamen
        $path = JPATH_BASE.DS.$this->params->get('path').DS.'Settings.php';

        if(!JFile::exists($path)) {

            return false;

        }

        require($path);

        $cookie = JRequest::getVar($cookiename, false, 'cookie');

        // und setzen das Cookie
        if ($cookie && preg_match('~^a:[34]:\{i:0;(i:\d{1,6}|s:[1-8]:"\d{1,8}");i:1;s:(0|40):"([a-fA-F0-9]{40})?";i:2;[id]:\d{1,14};(i:3;i:\d;)?\}$~', $cookie) === 1) {

            $array = @unserialize($cookie);

            if (isset($array[3])) {

                setcookie($cookiename, serialize(array(0, '', 0)), time() - 3600, '/', $this->params->get('domain'));

            }

        }

        $data = array($id, sha1($passwd.$salt), (time()+ $cookieTime*60), 0);

        setcookie($cookiename, serialize($data), (time()+ $cookieTime*60), '/', $this->params->get('domain'));

        $_SESSION['login_' . $cookiename] = $data;

        return true;
        
    }

Schlägt der Login fehl, wird einfach das Cookie gelöscht

Code
    public function onLoginFailure($response) {

        $app = JFactory::getApplication();

        if($app->isAdmin()) {

            return true;

        }

        // set database prefix
        $this->params->def('smfprefix', 'smf_');
        $this->params->def('path', 'forum');
        $this->params->def('domain');

        jimport('joomla.filesystem.file');

        $path = JPATH_BASE.DS.$this->params->get('path').DS.'Settings.php';

        if(!JFile::exists($path)) {

            return false;

        }

        require($path);

        unset($_COOKIE[$cookiename]);

        setcookie($cookiename, serialize(array(0, '', 0)), time() - 36000, '/', $this->params->get('domain'));

        $_SESSION['login_' . $cookiename] = serialize(array(0, '', 0));

        return true;

    }

LOGOUT:

Code
    public function onLogoutUser($parameters, $options) {

        $app = JFactory::getApplication();

        if($app->isAdmin()) {

            return true;

        }

        // set database prefix
        $this->params->def('smfprefix', 'smf_');
        $this->params->def('path', 'forum');
        $this->params->def('domain');

        jimport('joomla.filesystem.file');

        $path = JPATH_BASE.DS.$this->params->get('path').DS.'Settings.php';

        if(!JFile::exists($path)) {

            return false;

        }

        require($path);

        $cookie = JRequest::getVar($cookiename, false, 'cookie');

        unset($_COOKIE[$cookiename]);

        setcookie($cookiename, serialize(array(0, '', 0)), time() - 36000, '/', $this->params->get('domain'));

        $_SESSION['login_' . $cookiename] = serialize(array(0, '', 0));

        return true;

    }

Für den Logout wird auch das Cookie gelöscht.
« Letzte Änderung: 04. Oktober 2010, 11:51:46 von bembelimen »

bembelimen

  • Mitglied
  • **
  • Beiträge: 27
Re: Joomla (<)=> SMF 2.0
« Antwort #3 am: 04. Oktober 2010, 09:16:35 »
Platzhalter

Jorin

  • Gast
Re: Joomla (<)=> SMF 2.0
« Antwort #4 am: 20. Oktober 2010, 12:34:00 »
Wow! Auch wenn ich selbst nicht testen kann (und auch gar nicht möchte), herzlichen Dank für die Beschreibung!  :!.!:

 

Internes

Nutzungsbedingungen Impressum

Wissenswertes

Hilfe Knowledge Base

Nützliches

Downloads Socialmedia