Rebuild Single User Cache?

ScottLott

Member
Hey Guys,
I've got an addon that injects user group IDs into the XenForo database.

Unfortunately, when secondary user group IDs are injected the user's permissions are ruined preventing them from having access to any part of the forum.

I've narrowed the problem down to the "permission_combination_id" on the XenForo user table. When this value isn't set correctly, the user looses their permissions. (They have access to nothing, they can't even view the forums home page)

The (current) solution is to rebuild the user cache. This takes the existing user group IDs from the primary user group and secondary user groups and recalculates the permission combination ID.

But I can't just rebuild the user cache every 10 minutes, this is something that needs to happen on the fly as each user has their group IDs updated.

Knowing this, I narrowed the code down to the XenForo_DataWriter class. Turns out there is a function in there called "rebuildPermissionCombinationId" that is ran to do the magic.

How would I call the "XenForo_DataWriter" class from a PHP script outside of XenForo? I'm also having trouble figuring out what variables need to be fed into XenForo_DataWriter for it to know which user to rebuild.

Thanks!
 
I'm not sure how much of the following code is absolutely necessary for what you're trying to do, but generally external scripts use the following few lines initially to initiate XenForo:

PHP:
<?php

$startTime = microtime(true);
$fileDir = dirname(__FILE__);

require($fileDir . '/library/XenForo/Autoloader.php');
XenForo_Autoloader::getInstance()->setupAutoloader($fileDir . '/library');

XenForo_Application::initialize($fileDir . '/library', $fileDir);
XenForo_Application::set('page_start_time', $startTime);

$deps = new XenForo_Dependencies_Public();
$deps->preLoadData();

You will of course have to alter the various paths so they are correct in relation to your script.

From there, you can access a DataWriter like this:

PHP:
$writer = XenForo_DataWriter::create('XenForo_DataWriter_User');

You can specify existing data like this:

PHP:
$writer->setExistingData($userId);

Then, you can run your function like this:

PHP:
$writer->rebuildPermissionCombinationId();

Finally, you may need to save, especially if you've made any other changes in the writer:

PHP:
$writer->save();

Which, incidentally, brings me to another point.

That rebuild is probably automatically triggered when other data is changed in the DataWriter.

If you're making changes to the data in the user record, and you use the DataWriter to do it, then the rebuild may trigger automatically in the preSave() or postSave() functions.
 
Alright, new problem.

The application I'm working with uses the Zend framework (aMember), just like XenForo. Setting up the code above produces this error:
Exception_Zend: "Registry is already initialized"

I spent the past few days trying to extract the code from XenForo that calculates the Permission Combination ID without success.

I did some digging about the Zend framework and found the "addModuleDirectory()" function, I tried to use that to load the XenForo modules into aMember without success.

I'm out of ideas.

Again, I'm trying to force a User Cache rebuild on specific users as their usergroups are modified by aMember. If there's another slick way of doing this I haven't thought of so far, I'm open to ideas. :)

Edit:
WOO! I got it working, here's how:
I setup a updateGroups.php file that contains this:
Code:
<?php   
    $user_Id = $_GET['id'];
        $startTime = microtime(true);
        $fileDir = "community";

        require($fileDir . '/library/XenForo/Autoloader.php');
        XenForo_Autoloader::getInstance()->setupAutoloader($fileDir . '/library');

        XenForo_Application::initialize($fileDir . '/library', $fileDir);
        XenForo_Application::set('page_start_time', $startTime);

        $deps = new XenForo_Dependencies_Public();
        $deps->preLoadData();

        $writer = XenForo_DataWriter::create('XenForo_DataWriter_User');
        $writer->setExistingData($user_Id);
        $writer->rebuildPermissionCombinationId();
?>

and dropped it into the root folder of the server.

Then I dropped the following code into the "setGroups" function inside the aMember/XenForo plugin. This function is called for database rebuilds as well as each time a user's groups are changed by aMember.

Code:
        $ch = curl_init('http://localhost/updateGroup.php?id=' . $record->user_id);
        curl_exec($ch);

I use cURL to pass the user's ID to a PHP file that processes the user's permissions and rebuilds them.

The only drawback is XenForo database rebuilds inside aMember take 4-5 times longer now. But that's a problem I can handle. :)

Thanks so much guys!
 
Last edited:
Thank You. I need this exact same thing, but things (like autoloader) have moved around on XF 2.2. Have you updated your code for the newer version?

Thanks in advance!
 
So I ended up solving this another way. I just queried all the permission_combo permutations for my various member levels and made a constant that would provide them based on amount. Not "elegant" but works great in my case since there are only eight different combos.

sj
 
Back
Top Bottom