Questions/Comments on "A guide to the SMF integration hooks"

Started by Orstio, May 26, 2007, 05:19:48 PM

Previous topic - Next topic

hlsantos

QuoteI haven't been on the SMF team for almost two years.  You're asking the wrong guy to make changes. :P

Oops, sorry.  Didn't mean that. :)

Your suggestion to just throw the exception worked perfectly:


function wcsmf_register($regOptions, $themeVars)
{
    // initialize context, if not already done
    require_once("wildcat.php");
    if (!WcInitialize()) {
      fatal_error(sprintf("Wildcat! Server Not found: Error %08X",
                       wcGetLastError()), false);
   }

    // check if registration name already exist
    $uname = $regOptions['username'];
    $ui = array();
    if (wcLookupName($uname,&$ui)) {
  fatal_error(sprintf("Wildcat! User Name Already Exist, try another: %s",
                       htmlspecialchars($uname)), false);
        return false;
    }
    return true;
}


So we don't need to modify stock code!  Wonderful! :)

QuoteWhat do you see as a potential for the username to fail in your system but not in SMF?

Well,  from experience in other 3rd party integration, like with InstantForum,  when the synchronization is worked out, to install it (SMF), I will need to provide a small utility to


  • scan the backend user database to add set a new hashpwd field to hold and set the SMF hash for the user password based on the user's current password, and
  • create the SMF user database from the existing backend database.

They would be in sync now for login and logout.

But with a new user SMF registration,  it needs to make sure that the backend user account name doesn't exist already before it creates one in SMF.   Otherwise, if not checked, the user would be able to login in SMF but the the backend. :)

The main reason it has to be in sync, is there are all sorts of monitoring tools, i.e. help desk, GUI, finger, text mode, etc that can be done from remote.   They need to see who's logged in, what they are doing (actiivity colum:  SMF Forum - XYZ).     I am going to have to also provide hooks for actions such as:

       - integrate_disconnect_user()

and other actions but not as important as the disconect which would log off the user at the back end and some how I need to see how to easily hooked in logic where a SMF PHP request will check if the user has been forced off and then force SMF to redirect to some logoff page perhaps.   The disconnect can be a manual operator request, system maintenance request and/or session time out.  They don't probably have to be 100% here once logged in, but the user can never be logged into SMF without being logged into the backend.

The only reason this has become desirable is because message system is 25 years old and currently lacks the "topical style" of mail displays and authentication is required to even read mail.   So we been losing a crowd of people to these new forum (i call them topical views) systems, but more and more are asking for the integration. I wish to satisfy that request and even since we did the PHP extension that ties in our server API, we been getting more PHP applications installed.  SMF fits right in and its must say its pretty spiffy. :)

I think people will be very excisted about it.  What worries me is that they will also want more to tie in the mail forums, conferences and security access profiles configrations as well and then I am going to figure out what that all means in terms of source code customization, licensing and all that.  So for now, starting with synchronizing the session management will suffice for independent operator SMF installation.  No changes to stock files.  Just provide the minimum extra hooking files.

Thanks for your wonderful assistance. :)

--


kaisifus

Hi Orsito,

You seem to be very much the man in the know for this sort of thing, and I thank you wholeheartedly for the extensive documentation and support provided that has, up until this point, prevented me from needing to pick your brains for the answers to my problems.

Currently, I am attempting to bridge SMF with the PHPMotion CMS on a completely fresh site, with PHPMotion serving as the main directory and SMF in a subdirectory 'forum'. I am using an include on the SMF index page named 'integrate.php' to begin the integration hooks. Currently, I have integrate_verify_user serving to log in and register users on SMF when they log in on to the CMS working well. I have begun working on the logout script using integrate_logout to unset the cookies used by the CMS, however no matter what I have tried, once the script executes and the page reloads, I am always still logged in on SMF & the CMS and the cookies are still present.

My integrate login function
function integrateLogout() {
if ( isset($_COOKIE[user]) && isset($_COOKIE[pass]) ){
$theUsername = $_COOKIE[user];

setcookie('user', '', time() - 3600*25);
setcookie('pass', '', time() - 3600*25);
                session_destroy();

mysql_select_db('complete_smf1');
$theIDQuery = mysql_query("SELECT ID_MEMBER FROM smf_members WHERE memberName = '" . $theUsername . "'");
$theUserID = mysql_fetch_row($theIDQuery);
$theUserID = $theUserID[0];

mysql_select_db('complete_cms');
$sql = "DELETE FROM online WHERE logged_in_id = $theUserID";
mysql_query($sql);
mysql_select_db('complete_smf1');
}
}


The logout routine used by the CMS is quite similar, I have tried copying it pretty much exactly for my own function, but still have exactly the same problem:

PHPMotion logout routine
$user_id = "";
$user_name = "";
$random_code = "";

@session_start();
@session_destroy();

setcookie('user', '', time() - 3600);
setcookie('pass', '', time() - 3600);

$sql = "DELETE FROM online WHERE logged_in_id = $user_id";
@mysql_query($sql);
@mysql_close();

//just to be extra sure - set sessions to NULL
$_SESSION['random_code'] = NULL;
$_SESSION['user_id'] = NULL;
$_SESSION['user_name'] = NULL;
$_SESSION["admin_logged"] = NULL;

header("Location: " . "index.php");


I Really don't know why this is happening, my only thought was that perhaps the integrate_validate_login function was executing when index.php refreshes on one of the session cookies and automatically logs in, although I have tried destroying the session data when I was using this code and had no luck..

Orstio


kaisifus

I nabbed it and adapted it slightly from another post on this thread... can't find the original right at this second.
verifyUser function:

function verfiyUser() {

//Is a user logged into CompletelyDark by Cookie?
if (!isset($_COOKIE['user'])) {
return;
} else {
$CDUsername = $_COOKIE['user'];
};

//Check if user is an SMF member
$getSmfUser = mysql_query("SELECT ID_MEMBER FROM smf_members WHERE memberName = '" . $CDUsername . "'");
if (mysql_num_rows($getSmfUser) >= 1) {
$smfUserRow = mysql_fetch_array($getSmfUser);
$smfUserID = $smfUserRow['ID_MEMBER'];
} else { //If not, add CompletelyDark user to SMF member list
//Get user's CompletelyDark  account info
mysql_select_db('complete_cms');
$getCDUser = mysql_query("SELECT * FROM member_profile WHERE user_name = '" . $CDUsername . "'");
if (mysql_num_rows($getCDUser) <= 0) {
return;
};
$row = mysql_fetch_array($getCDUser);
//Migrate user to SMF
$smfUserID = migrateToSMF($row['user_name'], $row['password'], $row['email_address']);
};

return $smfUserID;
};

function migrateToSMF($user_name, $password, $email_address){
$join_date = time();
mysql_select_db('complete_smf1');
mysql_query("
INSERT INTO smf_members
(memberName,
realName,
passwd,
emailAddress,
dateRegistered,
ID_POST_GROUP,
lngfile,
buddy_list,
pm_ignore_list,
messageLabels,
personalText,
websiteTitle,
websiteUrl,
location,
ICQ,
MSN,
signature,
avatar,
usertitle,
memberIP,
memberIP2,
secretQuestion,
additionalGroups)
VALUES
('$user_name',
'$user_name',
'$password',
'$email_address',
$join_date,
'4',
'',
'',
'',
'',
'',
'',
'',
'',
'',
'',
'',
'',
'',
'',
'',
'',
'')"
);
$memberID = mysql_insert_id();
mysql_query("UPDATE smf_log_activity SET registers = registers + 1 WHERE date ='" . date("Y-m-d") . "' LIMIT 1");
return $memberID;
}


Cheers for the quick support

Orstio

I'm going to assume you've been testing this with your admin account, and the ID_MEMBER of that admin account = 1?

Try this:

function verfiyUser() {

//Is a user logged into CompletelyDark by Cookie?
if (!isset($_COOKIE['user']) || $_COOKIE['user']=='') {
return false;
} else {
$CDUsername = $_COOKIE['user'];
}

//Check if user is an SMF member
$getSmfUser = mysql_query("SELECT ID_MEMBER FROM smf_members WHERE memberName = '" . $CDUsername . "'");
if (mysql_num_rows($getSmfUser) >= 1) {
$smfUserRow = mysql_fetch_array($getSmfUser);
$smfUserID = $smfUserRow['ID_MEMBER'];
} else {
//If not, add CompletelyDark user to SMF member list
//Get user's CompletelyDark  account info
mysql_select_db('complete_cms');
$getCDUser = mysql_query("SELECT * FROM member_profile WHERE user_name = '" . $CDUsername . "'");
if (mysql_num_rows($getCDUser) <= 0) {
mysql_select_db('complete_smf1');
return false;
}
$row = mysql_fetch_array($getCDUser);
//Migrate user to SMF
$smfUserID = migrateToSMF($row['user_name'], $row['password'], $row['email_address']);
}

mysql_select_db('complete_smf1');
return $smfUserID;
}

kaisifus

I have been testing with an account that I created using the verifyUser function with an ID of 3. I have checked and the user ID is the same on the CMS and SMF db's.

If it would help, I could give you the username and pass so you could log in and see the function executing?

Orstio

Do the code changes I suggested above make any difference?

kaisifus

Sorry, forgot to mention that... No, it still won't log out unfortunately.

With the integrateLogout function I was able to echo the username and ID, so they are being passed through fine, it just does not seem to be getting rid of the cookie for some reason.

I was also having a little difficulty in accessing the variables that are actually passed to the hook functions, for example in the integrate_delete_member, I need the ID_MEMBER variable, but have no idea how to call it at the moment?


kaisifus


Orstio

Well, the ID_MEMBER should be passed to the function.  Can I see your code for it?

kaisifus

integrateDeleteMember Function
function integrateDeleteMember($user_id) {
mysql_select_db('complete_cms');
// member profile
$sql = "DELETE FROM member_profile WHERE user_id = $user_id";
@mysql_query($sql);

//videos
$sql = "UPDATE videos SET approved ='pendingdelete' WHERE user_id = $user_id";
@mysql_query($sql);
}


there is more, but they are all SQL delete statements in the same format for the various tables of the database.

I still have had no luck with the logout function. It just seems like a relatively simple bit of code to cause such a headache heh...

Orstio

In all your functions, when it's done, you need to select the database back to the SMF database before return.

And in stuff like this:

if ( isset($_COOKIE[user]) && isset($_COOKIE[pass]) ){

I'd make sure there are quotes.

if ( isset($_COOKIE['user']) && isset($_COOKIE['pass']) ){

Simple things can sometimes cause major headaches.

kaisifus

Heh, woke up and approached it with a fresh head this afternoon.

Turns out my problem was that I was not setting the cookie paths. I thought that they defaulted to the same value anyway, but making the change from this:
setcookie('user', '', time() - 3600*25);
to this:
setcookie('user', '', time() - 3600*25,'/');
Made all the difference.

I also had to delete my PHPSESSID cookie with another setcookie command. I hope this can help someone who is stuck.

kaisifus

The PHPMotion integration seems to be going well. So far, the registrations, login, logout, reset pass and delete member hooks seem to be running nicely, and I am currently working on integrate_change_member_data.

Which brings me to my next question...

I can't for the life of me think of a method to iterate through each user name, co-ordinating the given variables and data to the relevent fields on the CMS db, given that at any point the $vars and $data could be in a different arrangement depending on what has been supplied to the hook and that the CMS member DB has different field names...

So far I am looping through each name:
foreach($memberNames as $theMember){

Changing the details here would be easy if every time the same $vars are given, but from my experience this hook only seems to return those that have been edited?

Orstio

You would do best to set up an association array, and change the associated variable in PHPMotion.

$synch_fields = array(
    'memberName' => 'username',
'realName' => 'name',
'emailAddress' => 'email',
'ID_GROUP' => '',
'gender'=>'',
'birthdate'=>'',
'websiteTitle'=>'',
'websiteUrl'=>'',
'location'=>'',
'hideEmail'=>'',
'timeFormat'=>'',
'timeOffset'=>'',
'avatar'=>'',
'lngfile'=>'',
);

$field_to_change = $synch_fields[$var];


The hook passes only one $var at a time, so even if there are multiple members getting edited at once, the hook contains only a single field.  The only instance in which I can see multiple members getting edited at once is in the group edits in the admin panel, actually.

kaisifus

For some reason, the integrate_change_member_data hook only seems to be passing me the gender result set? I really don't know why as I have tried simply printing the output, and all that ever returns is gender 0?

I am pretty sure that once I can figure out why it is doing this, I may have a working function

function integrateChangeMemberData($memberNames,$var,$data){
mysql_select_db('complete_cms');
$synch_fields = array(
'memberName' => 'user_name',
'realName' => 'first_name',
'emailAddress' => 'email_address',
'ID_GROUP' => '',
'gender'=>'gender',
'birthdate'=>'birthday',
'websiteTitle'=>'',
'websiteUrl'=>'personal_website',
'location'=>'current_country',
'hideEmail'=>'',
'timeFormat'=>'',
'timeOffset'=>'',
'avatar'=>'',
'lngfile'=>'',
'personalText' => 'about_me',
);

for($x=0;$x<count($memberNames);$x++){
for($y=0;$y<count($memberNames);$y++){
for($z=0;$z<count($var);$z++){
$query = mysql_query("UPDATE FROM member_profile SET '$synch_fields[$var]' = $data  WHERE user_name = '$memberNames[$y]'") or die(mysql_error());
}
       }
        }
mysql_select_db('complete_smf1');
}



Orstio

Wow, you have way too many for loops there.

$var is a single variable, so no need to loop it.


$field_to_change = $synch_fields[$var];

foreach ($memberNames as $memberName){
     $query = mysql_query("UPDATE member_profile SET `".$field_to_change."` = '{$data}'  WHERE user_name = '{$memberName}'") or die(mysql_error());   

}

kaisifus

Thanks so much for your time and patience Orstio, without it this project would have been a lot more difficult..

I have pretty much finished the integration now, just a couple of little hangups on things that I can't work out still.

integrate_change_member_data
function integrateChangeMemberData($memberNames,$var,$data){
$synch_fields = array(
'memberName' => 'user_name',
'realName' => 'first_name',
'emailAddress' => 'email_address',
'ID_GROUP' => '',
'gender'=>'gender',
'birthdate'=>'birthday',
'websiteTitle'=>'',
'websiteUrl'=>'personal_website',
'location'=>'current_country',
'hideEmail'=>'',
'timeFormat'=>'',
'timeOffset'=>'',
'avatar'=>'',
'lngfile'=>'',
'personalText' => 'about_me',
);

$field_to_change = $synch_fields[$var];
mysql_select_db('complete_cms');
foreach ($memberNames as $memberName){
    $query = mysql_query("UPDATE member_profile SET `".$field_to_change."` = '{$data}'  WHERE user_name = '{$memberName}'") or die(mysql_error());   
}
mysql_select_db('complete_smf1');
}


For some reason, every time this gets stuck on the birthday and returns:

for the right syntax to use near '0001-01-01'' WHERE user_name = 'testmotion'' at line 1

Looking at the message, each variable has a single quote' first followed by a double", which could be causing the problem... I have tried various combinations of quotes etc and have had no luck what so ever. Even when I leave the function out of the hook, it still gets caught. Both fields are set as DATE as well on the database, so they should at least be compatible....

Also, my integrate_personal_message function has a problem:
function integratePersonalMessage ($recipients,$from,$subject,$message){
foreach ($recipients as $recipient){
$recipientSMFID = $recipient[0];
mysql_select_db('complete_smf1');
$recipientQuery = mysql_query("SELECT memberName FROM smf_members WHERE ID_MEMBER = '$recipientSMFID'");
$recipientResult = mysql_fetch_array($recipientQuery);
$recipientName = $recipientResult[0];

mysql_select_db('complete_cms');
$IDquery = mysql_query("SELECT user_id FROM member_profile WHERE user_name = '$recipientName'");
$cmsRecipientResult = mysql_fetch_array($IDquery);
$recipientID = $cmsRecipientResult[0];

$senderIDQuery = mysql_query("SELECT user_id FROM member_profile WHERE user_name = '$from'");
$cmsSenderID = mysql_fetch_array($senderIDQuery);
$senderID = $cmsSenderID[0];

$sql = "INSERT into messages (from_username, subject, message, todays_date, to_id) VALUES ('$from', '$subject', '$message', NOW(), '$recipientID')";
mysql_query($sql);
// record a copy in sent items box
$sql = "INSERT into messages_sent (to_username, subject, message, todays_date, from_id) VALUES ('$recipientName', '$subject', '$message', NOW(), '$senderID')";
mysql_query($sql);
}
mysql_select_db('complete_smf1');
}


it copies the message over to both databases successfully, but then dies with an error "8: Undefined offset: 0" which I believe is referring to the $recipient array. I think the problem is that for some reason it is trying to loop past the amount of recipients for the message.

With these last two problems ironed out, the basic integration will be finished. As soon as I have been marked (I am working on this as part of a uni project), I will post this back on here in the hopes that it will be useful to someone on here.



Orstio

Try this one instead?

$query = mysql_query("UPDATE member_profile SET `".$field_to_change."` = {$data}  WHERE user_name = {$memberName}") or die(mysql_error());

Sometimes fields are passed from SMF already with single quotes in the string.

As for your integratePersonalMessage, $recipient is not an array.  You have taken the $recipients array, and made it foreach($recipients as $recipient), so each $recipient is a single variable, not an array of values.

Advertisement: