1. This site uses cookies. By continuing to use this site, you are agreeing to our use of cookies. Learn More.

How to Create a Bridge?

Discussion in 'XenForo Development Discussions' started by JaredNZ, Feb 28, 2012.

  1. JaredNZ

    JaredNZ Member

    Hello,

    I'm trying to integrate XF into my login system (a web-application). XF looks pretty big & complex; can someone please give a step by step guide how I can:

    1. When a user signs-up to my web-app, what do I need call/insert into XF tables to register the user signed up? Including setting the user's image.

    2. When a user signs-in into my site; what do I need to call to register the user logged in on the forums?

    3. Use the header and footer of my website; when I try and "include()" the forum index.php it redirects the browser.

    Thank you!
     
  2. Jake Bunce

    Jake Bunce XenForo Moderator Staff Member

  3. JaredNZ

    JaredNZ Member

    Thanks for your reply Jake; but "look at the code" doesn't really answer my questions. Its still huge and complex and I don't see any function "AddUser" in the User.php. Can you please break it down step by step; I'm sure it'll be useful for future forum users. And e.g. To use the datawriter/actionLogin() it looks like there is a huge chain of function dependencies.

    1,2. Starting from the beginning.. from an external application, how can we add a user using datawriter? and login. Otherwise I'll spend days tracing all the dependencies and hacking XF apart.

    3. This looks like for a single page? I need the whole forum to appear in an existing site. Not my header inside their layout.
     
  4. Jake Bunce

    Jake Bunce XenForo Moderator Staff Member

    It does require programming. I can point to code examples but it still amounts to "looking at the code". I don't have anything written and tested for your specific application. I have never attempted external registrations myself.

    3) In that case you should modify XF's style to look like the rest of your site. The "body" template hook will probably be useful if you want to wrap the forum in some other layout. Create a listener for the template_hook event:

    Admin CP -> Development -> Code Event Listeners

    Use code like this:

    Code:
    <?php
    
    class Class_Name
    {
    	public static function bodyWrapper($hookName, &$contents, array $hookParams, XenForo_Template_Abstract $template)
    	{
    		if ($hookName == 'body')
    		{
    			$contents = 'top wrapper stuff' . $contents . 'bottom wrapper stuff';
    		}
    	}
    }
    
    You can add your own includes in there if you need to use a layout file.
     
    Adam K M and trichome like this.
  5. JaredNZ

    JaredNZ Member

    I do not have any "Development" link in the Admin Control Panel, I am using v1.1. Am I looking in the right place?
     
  6. Brogan

    Brogan XenForo Moderator Staff Member

    How do I enable the Development tab in the Admin Control Panel?
    Debug mode must be enabled.


    How do I enable debug mode?
    Edit the library/config.php file and add the following:
    PHP:
    $config['debug'] = true;
    Note that it is not advised to run a live site with debug mode enabled due to the increased resources utilised.

    http://xenforo.com/community/threads/frequently-asked-questions.5183/#post-248490
     
    LPH likes this.
  7. JaredNZ

    JaredNZ Member

    Thank you for replies; can I add a lone listener like this or will I need to create an add-on and put the listener inside an add-on? (like this tutorial for creating an add-on; and uses a listener)

    i.e. - Where does this class/file go within the "2,541 Files, 505 Folders" that are under /xf/ ?

    Perhaps a new tutorial would be handy "How to create an event listener"

    The more you help people integrate the forum into existing businesses the most clients you'll get.
     
  8. Brogan

    Brogan XenForo Moderator Staff Member

  9. JaredNZ

    JaredNZ Member

    For future readers: you can add an event listener with-out an add-on, by putting a raw PHP file, same name as your class under the /forum/library directory
     
    PyroM and creativeforge like this.
  10. JaredNZ

    JaredNZ Member

    I searched that forum Brogan. There were no tutorials about adding an event listener.
     
  11. JaredNZ

    JaredNZ Member

    So "Step by Step" for #3: For future readers...

    - Enable Debug Mode in Config.php with $config['debug'] = true;
    - Create a file "MySiteWrapper.php" put it under /forum/library
    - Inside MySiteWrapper.php put:
    class MySiteWrapper
    {
    public static function bodyWrapper($hookName, &$contents, array $hookParams, XenForo_Template_Abstract $template)
    {
    if ($hookName == 'body')
    {
    $contents = 'top wrapper stuff' . $contents . 'bottom wrapper stuff';
    }
    }
    }

    - Go to your control panel under development/add event Listener
    - Add an even listener with "template_hook"
    - Set class: MySiteWrapper, and Method: bodyHook.

    #3 DONE!
     
    PyroM and Claudio like this.
  12. JaredNZ

    JaredNZ Member

    #0: How to use XenForo Objects: Once initialized the below you can use XenForo objects:
    PHP:
    define('XF_ROOT'$_SERVER['DOCUMENT_ROOT'] . '/forum'); // set this!
    define('TIMENOW'time());
    define('SESSION_BYPASS'false); // if true: logged in user info and sessions are not needed
    require_once(XF_ROOT '/library/XenForo/Autoloader.php');
    XenForo_Autoloader::getInstance()->setupAutoloader(XF_ROOT '/library');
    XenForo_Application::initialize(XF_ROOT '/library'XF_ROOT);
    XenForo_Application::set('page_start_time'TIMENOW);
    XenForo_Application::disablePhpErrorHandler();
    XenForo_Session::startPublicSession();
    error_reporting(E_ALL & ~E_NOTICE); // Turn off the strict error reporting.

    #1 : How to Create a User Using the DataWriter: Run the initialization above, then run the code below; (set the $data and $password arrays, it was in Register.php):
    PHP:
    //Set User Data
    $data = array(
    'username'  => "jaredNZ",
    'email'      => "jared@email.com",//Must be unique
    'timezone'  => "Europe/London",//Country/City
    'gender'    => "male",//Gender in english
    'dob_day'    => 12,
    'dob_month'  => 12,
    'dob_year'  => 1980,
    );
     
    //Set Raw Passwords
    $passwords = array('password' => "testing"'password_confirm' => "testing");
     
    //Get the default options from XenForo.
    $options XenForo_Application::get('options');
     
    //Create the dataWriter object, set the defaults.
    $writer XenForo_DataWriter::create('XenForo_DataWriter_User');
    if (
    $options->registrationDefaults) {
    $writer->bulkSet($options->registrationDefaults, array('ignoreInvalidFields' => true));
    }
    $writer->bulkSet($data);
    $writer->setPassword($passwords['password'], $passwords['password_confirm']);
     
    //If the email corresponds to an existing Gravatar, use it
    if ($options->gravatarEnable && XenForo_Model_Avatar::gravatarExists($data['email'])) {
    $writer->set('gravatar'$data['email']);
    }
     
    //Save the User to Database:
    $writer->set('user_group_id'XenForo_Model_User::$defaultRegisteredGroupId);
    $writer->set('language_id'XenForo_Visitor::getInstance()->get('language_id'));
    $writer->advanceRegistrationUserState();
    $writer->preSave();
    $writer->save();
     
    //Get the User as a Variable:
    $user $writer->getMergedData();
     
    //Log the ip of the user registering
    XenForo_Model_Ip::log($user['user_id'], 'user'$user['user_id'], 'register');
     
    //Set the user back to the browser session
    XenForo_Application::get('session')->changeUserId($user['user_id']);
    XenForo_Visitor::setup($user['user_id']);
     
    sathkur, Wysie, vbezruchkin and 2 others like this.
  13. Deepmartini

    Deepmartini Well-Known Member

    I'm wondering if this code is a good place to start? Is there a way to set this up easily via wordpress so that when a user is logged in via wordpress, a user account can be created in Xenforo and they will already be logged in? I have seen mods that go the other way around Xenforo to Wordpress but none so far that are Wordpress to Xenforo. This might be enough to help get me started.
     
  14. JaredNZ

    JaredNZ Member

    Hey Deep;

    Yes this should do what you need

    I am working on the login code ( #2 ) now. I will post it tomorrow,

    Jared
     
  15. JaredNZ

    JaredNZ Member

    After much searching I stumbled on this thread : http://xenforo.com/community/threads/user-class.16846/ and copied some of the routines, and added a few bits:
    PHP:
    //Create and login a new user
    appXenForo::createUser("jared""jared@email.com""testing");
    //Login an existing user
    appXenForo::login("jared@email.com""testing");
    appXenForo::setLogin($iXFID)
    //Kill all loggedin sessions
    appXenForo::logout();
    //Get user information
    appXenForo::getCurrentUser();
    appXenForo::getUserByEmail("jared@email.com");
    //Change User Password
    appXenForo::setPassword(12"newPassword");
    //Link to the Threads:
    appXenForo::getThreadURL("Thread Name"12);
    appXenForo::getForumURL("Forum Name"14);
    //Get the latest posts: (needs minimum 2 posts in board)
    appXenForo::getLatestPosts($iMax)
    //Check if the username is already in use:
    appXenForo::bUsernameInUse($sUsername);
    //Get all posts by a certain user:
    appXenForo::getMyPosts($iXFID$iLimit);
    Using only one hack :unsure:
    In Session.php // function set(): comment out the line "throw new XenForo_Exception('The session has been saved and is now read-only.');"

    Using the class:
    PHP:
    <?php
    /*
    *XenForo Support Functions
    */
    class appXenForo {
     
    //Singleton Storage
    private static $bInitialized false;
     
    /**
    * Initialise XenForo Functions
    */
    function __construct(){}
     
    /*
    *Initialise the XenForom Controllers:
    */
    public static function init() {
    define('XF_ROOT'$_SERVER['DOCUMENT_ROOT'] . '/forum'); // set this!
    define('TIMENOW'time());define('SESSION_BYPASS'false);
    require_once(
    XF_ROOT '/library/XenForo/Autoloader.php');
    XenForo_Autoloader::getInstance()->setupAutoloader(XF_ROOT '/library');
    XenForo_Application::initialize(XF_ROOT '/library'XF_ROOT);
    XenForo_Application::set('page_start_time'TIMENOW);
    XenForo_Application::disablePhpErrorHandler();
    XenForo_Session::startPublicSession();
    error_reporting(E_ALL & ~E_NOTICE);
    }
     
     
    /*
    *Set the user password.
    */
    public static function setPassword($iID$sPassword) {
     
    $sQuery "UPDATE xf_user_authenticate SET data = BINARY
    CONCAT(CONCAT(CONCAT('a:3:{s:4:\"hash\";s:40:\"',
    SHA1(CONCAT(SHA1('
    $sPassword'), SHA1('salt')))),
    CONCAT('\";s:4:\"salt\";s:40:\"', SHA1('salt'))),
    '\";s:8:\"hashFunc\";s:4:\"sha1\";}'),scheme_class = 'XenForo_Authentication_Core'
    WHERE user_id = 
    $iID;";
     
    query($sQuery);
    }
     
     
    /*
    *Get an array of the latest posts:
    */
    public static function getLatestPosts($iMax) {
    $sQuery "
    SELECT thread.last_post_id as post_id,
    thread.last_post_user_id as user_id,
    thread.last_post_username as username ,
    thread.discussion_state,
    thread.last_post_date,
    thread.title as threadtitle,
    thread.thread_id as thread_id,
    forum.title as node_title, forum.node_id as node_id
    FROM xf_thread as thread
    LEFT JOIN xf_node as forum ON (forum.node_id = thread.node_id)
    ORDER BY thread.last_post_date DESC
    LIMIT 
    $iMax";
     
    //Get the rows:
    $aLatest query($sQuery);
     
    // Loop over each post, get the message
    foreach ($aLatest as &$cPost) {
    //Get the message:
    $aRow query("SELECT * FROM xf_post WHERE post_id = ? LIMIT 1", array($cPost['post_id']));
    $cPost['message'] = self::stripBBCode($aRow['message']);
     
    }
     
    return 
    $aLatest;
    }
     
     
     
    /*
    *Get a filtered string URL for the thread:
    */
    public static function getThreadURL($sThreadTitle$iThreadID) {
    $sThreadURL strtolower(str_replace(" ""-"$sThreadTitle));
    $sThreadURL preg_replace("/[^A-Za-z0-9-]/",''$sThreadURL);
    return 
    "forum/index.php?threads/{$sThreadURL}.{$iThreadID}";
    }
     
     
    /*
    *Get a filtered string URL for a node / forum/.
    */
    public static function getNodeURL($sNodeTitle$iNodeID) {
    $sForumURL strtolower(str_replace(" ""-"$sNodeTitle));
    $sForumURL preg_replace("/[^A-Za-z0-9-]/",''$sForumURL);
    return 
    "forum/index.php?forums/{$sNodeURL}.{$cPost['node_id']}/";
    }
     
     
    /*
    *Strip out BB code from xfPosts
    */
    public static function stripBBCode($sMessage) {
    return 
    strip_tags(str_replace(array('[',']'), array('<','>'), $sMessage));
    }
     
     
    /*
    *Create a XenForo User:
    */
    public static function createUser($sUsername$sEmail$sPassword, array $aAdditionalData = array()) {
     
    //Create the username from the person's name:
    $sUsername str_replace(' '"_"$sUsername);
     
    //Set User Data
    $cWriter XenForo_DataWriter::create('XenForo_DataWriter_User');
    $cWriter->set('username'$sUsername);
    $cWriter->set('email'$sEmail);
    $cWriter->setPassword($sPassword);
    $cWriter->set('user_group_id'XenForo_Model_User::$defaultRegisteredGroupId);
    $cWriter->set('user_state''email_confirm');
    foreach (
    $aAdditionalData AS $data => $key) {
    $cWriter->set($data$key);
    }
    $cWriter->save();
    $cUser $cWriter->getMergedData();
     
    //Login new user: Log the ip of the user registering
    XenForo_Model_Ip::log($cUser['user_id'], 'user'$cUser['user_id'], 'register');
    //Set the user back to the browser session
    XenForo_Application::get('session')->changeUserId($cUser['user_id']);
    XenForo_Visitor::setup($cUser['user_id']);
     
    return 
    $cUser['user_id'];
    }
     
     
    /*
    *Get the current user:
    */
    public static function getCurrentUser() {
     
    XenForo_Session::startPublicSession();
    $cVisitor XenForo_Visitor::getInstance();
    if(
    $cVisitor->getUserId()){
    $dbUserModel XenForo_Model::create('XenForo_Model_User');
    $cUserInfo $dbUserModel->getFullUserById($cVisitor->getUserId());
    }
    return 
    $cUserInfo;
    }
     
     
     
    /*
    *Get the current user:
    */
    public static function getUserByEmail($sEmail) {
    $dbUserModel XenForo_Model::create('XenForo_Model_User');
    $cUser $dbUserModel->getUserByEmail($sEmail, array('join' => XenForo_Model_User::FETCH_USER_PROFILE XenForo_Model_User::FETCH_LAST_ACTIVITY));
    return 
    $cUser;
    }
     
     
    /*
    *Set the user state: from email_confirm to valid.
    */
    public static function setUserState($iXFID$sState) {
    //'valid'
    //'email_confirm'
     
    //query("UPDATE xf_user SET user_state = ? WHERE user_id = ? LIMIT 1", array($sState, $iXFID));
    }
     
     
    /*
    *Login a XenForo User // Set the cookie.
    */
    public static function login($sEmail$sPassword$bRemember true) {
     
    //Get this class; delete existing login information
    error_reporting(E_ALL);
    restore_error_handler();
    restore_exception_handler();
     
    $dbLoginModel XenForo_Model::create('XenForo_Model_Login');
    $dbUserModel XenForo_Model::create('XenForo_Model_User');
    $sError "";
     
    $iUserID $dbUserModel->validateAuthentication($sEmail$sPassword$sError);
    if (!
    $iUserID) {
    $dbLoginModel->logLoginAttempt($sEmail);
    return 
    $sError;
    }
     
    $dbLoginModel->clearLoginAttempts($sEmail);
     
    if (
    $bRemember) {
    $dbUserModel->setUserRememberCookie($iUserID);
    }
     
    XenForo_Model_Ip::log($iUserID'user'$iUserID'login');
     
    $dbUserModel->deleteSessionActivity(0$_SERVER['REMOTE_ADDR']);
     
    $cSession XenForo_Application::get('session');
    $cSession->changeUserId($iUserID);
    XenForo_Visitor::setup($iUserID);
     
    return 
    $iUserID;
    }
     
     
    /*
    *Set this user ID as logged in.
    */
    public static function setLogin($iUserID) {
    $dbUserModel XenForo_Model::create('XenForo_Model_User');
    $dbUserModel->setUserRememberCookie($iUserID);
    XenForo_Model_Ip::log($iUserID'user'$iUserID'login');
    $dbUserModel->deleteSessionActivity(0$_SERVER['REMOTE_ADDR']);
    $cSession XenForo_Application::get('session');
    $cSession->changeUserId($iUserID);
    XenForo_Visitor::setup($iUserID);
    }
     
     
    /*
    *Check if this user name is in use, return true for already exists.
    */
    public static function bUsernameInUse($sUsername) {
    $cUser query("SELECT * FROM xf_user WHERE `username` =  '$sUsername' LIMIT 1");
     
    if (
    is_numeric($cUser['user_id'])) {
    return 
    true;
    } else {
    return 
    false;
    }
    }
     
     
     
    /*
    *Get all the posts belonging to this user: with this XFID, and limit X posts:
    */
    public static function getMyPosts($iXFID$iLimit 10) {
    //Get the rows:
    $aMyPosts query("SELECT * FROM xf_post WHERE user_id = ? ORDER BY post_date DESC LIMIT ?", array($iXFID$iLimit));
    //$aMyPosts = appDB::arrayify($aMyPosts); //Check is array for the foreach.
    // Loop over each post, get the thread information:
    foreach ($aMyPosts as &$cPost) {
    //Process the BB code out:
    $cPost['message'] = self::stripBBCode($cPost['message']);
    //Get the Thread Title:
    $cPost['thread'] = query("SELECT * FROM xf_thread WHERE thread_id = ? LIMIT 1", array($cPost['thread_id']));
    //Get Forum Info:
    $cPost['forum'] = query("SELECT * FROM xf_forum WHERE node_id = ? LIMIT 1", array($cPost['thread']['node_id']));
    }
    return 
    $aMyPosts;
    }
     
     
    /*
    *Delete the current session and log out.
    */
    public static function logout() {
    if (
    XenForo_Visitor::getInstance()->get('is_admin')) {
    $adminSession = new XenForo_Session(array('admin' => true));
    $adminSession->start();
    if (
    $adminSession->get('user_id') == XenForo_Visitor::getUserId()) {
    $adminSession->delete();
    }
    }
    XenForo_Model::create('XenForo_Model_Session')->processLastActivityUpdateForLogOut(XenForo_Visitor::getUserId());
    XenForo_Application::get('session')->delete();
    XenForo_Helper_Cookie::deleteAllCookies(
              array(
    'session'),
              array(
    'user' => array('httpOnly' => false))
          );
    XenForo_Visitor::setup(0);
    return;
    }
    }
    ?>
    I welcome suggestions to avoid the hack

    [Edit] - 4/3/12 Bug fix in login routine + Moved init routines out of function, they need to be declared in global context.... Moved it back in so future people can see init() code.

    [Edit] - 4/3/12 Added appXenForo::setLogin($iUserID) to bypass login routines and use your own login system (e.g. add setLogin when WordPress declares the user authenticated.)

    [Edit] - 4/3/12 Lucas Bug fix getUserByEmail()

    [Edit] - 6/3/12 Added Jake's User Password Reset Query

    [Edit] - 7/3/12 Added Forum/Thread URL's
    7/3/12 Added getLatestPosts(iMax)
    7/3/12 Added bUsernameInUse()

    [Edit] - 22/4/12 Added getMyPosts() - Get all posts + thread information for a certain User ID. Put it into a giant array for display
     
    PyroM, nacs and Jake Bunce like this.
  16. Lucas Young

    Lucas Young New Member

    Hi Jared
    This is fantastic, thank you :)
    I'm integrating this into a site now. I'd love to know if you could suggest ways to implement two other functions:
    1. changePassword - for when a user changes their password on a site, their xenForo password is updated as well
    2. deleteUser - if a user is deleted from a site, it can be removed from XenForo as well

    Cheers

    Lucas
     
  17. JaredNZ

    JaredNZ Member

    Cheers & Good ideas Lucas, I haven't gotten to that part yet - please post here if you do make those routines and I'll edit the class again to add them in, and still need a solution to the Session.php hack

    appXenForo::deleteUser() should be easy enough - its a link in the Admin CP // List All Users -> Click the 'cross' to delete user and trace back what code it runs.
     
    Jake Bunce likes this.
  18. Lucas Young

    Lucas Young New Member

    Thanks Jared - this should end up being a really useful class for many people.
    By the way I found a bug - in your function getUserByEmail($sEmail)
    It should be

    PHP:
    $dbLoginModel XenForo_Model::create('XenForo_Model_User');
    not

    PHP:
    $dbLoginModel XenForo_Model::create('XenForo_Model_Login');
    So far the class is working great :)
     
    Jake Bunce likes this.
  19. JaredNZ

    JaredNZ Member

    Thanks Lucas :) Modified the code & updated the whole routine to keep variable inline with naming convention (e.g. $dbUserModel == XenForo_Model_User)
     
  20. JaredNZ

    JaredNZ Member

    Added to class above - setPassword (thanks Jake), stripBBCode(), getForumURL() and getThreadURL(). Awaiting permission from Ragtek to post getRecentPosts()
     

Share This Page