How to Create a Bridge?

  • Thread starter Thread starter Deleted member 15541
  • Start date Start date
D

Deleted member 15541

Guest
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!
 
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.
 
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.
 
I do not have any "Development" link in the Admin Control Panel, I am using v1.1. Am I looking in the right place?
 
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.
 
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
 
I searched that forum Brogan. There were no tutorials about adding an event listener.
 
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!
 
#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']);
 
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.
 
Hey Deep;

Yes this should do what you need

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

Jared
 
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
 
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
 
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.
 
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 :)
 
Thanks Lucas :) Modified the code & updated the whole routine to keep variable inline with naming convention (e.g. $dbUserModel == XenForo_Model_User)
 
Added to class above - setPassword (thanks Jake), stripBBCode(), getForumURL() and getThreadURL(). Awaiting permission from Ragtek to post getRecentPosts()
 
Top Bottom