Sunday, April 5, 2015

How To Programmatically Add a Joomla User - Custom PHP Script - Joomla 2.5


In developing our incredible legal billing and client management system, ClientMatters, one of the tasks we had to accomplish was making it really easy for law firm administrators to authorize clients to log in to the client portal. ClientMatters is a legal billing and client management system built on the Joomla! platform. After looking at lots of out-of-date articles, here's what we came up with. This is an excerpt from a controller that handles a lot of administrative tasks.
< ? php
defined ('_JEXEC') or die('Restricted access.');

jimport('joomla.application.component.controller');

class BillingControllerSession extends JController
{
  public function __construct()
  {
     parent::__construct();
  }
 
  public function authorizeClient()
  {
    $result = null;
  
    try
    {
        //We need these 4 parameters. In our system,
        //it's an HTML form that collects information
        //from the law-firm administrator
        //and then sends it to this JController. 
  $username = $this->getParameter('u');
 $password = $this->getParameter('p');
  $email    = $this->getParameter('e');
  $name     = $this->getParameter('n');
   
  $user = array();
  $user['username']  = $username;
  $user['password']  = $password;
  $user['password2'] = $password;
  $user['sendEmail'] = 1;
  $user['usertype']  = 'CM Client';
        // 2 is the "Registered" group. You can add others.
  $user['groups']    = array(2); 
  $user['name']      = $name;
  $user['email']     = $email;
   
  $params = array();
  $params['timezone'] = 'America/Chicago';
  $params['language'] = 'en-GB';
        //You could add an item to this array containing
        //a key that links this Joomla! user record to a
        //related record in your solution's database. For
        //example, had we received the client's ID in the
        //HTML Request, we could add that here as follows:
        //   $params['clientid'] = $clientId;
   
  $user['params'] = $params;
  
  $instance = JUser::getInstance();
   
 if (!$instance->bind($user))
    throw new Exception('SESSION.PHP: Failed to bind user object. ('.
    $instance->getError().')');

        //Read MORE about this save method below!!
  if (!$instance->save(false))
    throw new Exception('SESSION.PHP: Failed to save user object. ('.
    $instance->getError().')');
     
 $result = array('status'=>1,
          'username'=>$username,'userid'=>$instance->id);
    }
    catch(Exception $e)
    {
  $result=array('status'=>0,'reason'=>$e->getMessage());
    }
  
    $this->reportResult($result);
  }

  //This function just catches programming errors.
  private function getParameter($variable, $default=false, $required=true)
  {
    $result = JRequest::getString($variable, $default);
    if (!$result && $required)
      throw new Exception("SESSION.PHP: Required parameter '$variable' is missing.");
    else
      return $result;
  }

  //Method to report the result of the operation to the caller as a json string. 
  private function reportResult($result)
  {
    if ($result['status'] == 0)
    {
  error_log('Error in session.php: '.$result['reason']);
    }
  
    $document =& JFactory::getDocument();
    $document->setMimeEncoding('application/json');
    JResponse::setHeader('Content-disposition', 'attachment;filename="'.$this->getName().'.json"', true);
    echo json_encode($result);
  }
}

The JUser::save() method blows up

Here's something that made me curse for a few HOURS one night as I was testing. The first time I tried to authorize a user, it worked great. Then I made some changes, deleted the user from the user's table through the Joomla! Administrator interface, and tried again. As soon as I tried to authorize the same client again, which is a valid use case, the Gantry framework that we use started complaining about not being able to find the JHelperModule. Whaaaaat?!

The user record was being added OK by the
$instance->save()
method, but the error regarding the JHelperModule was so deep in the framework, that when it blew up, it thwarted our try/catch and no useful result was sent back to the browser. The user record was being added and the client could log in, but there was no way to let the user know the call succeeded, at least until the client attempted to login.

Because the error was raised in the Gantry framework, I wanted to conform or eliminate Gantry as the problem. To do that, I changed the template to one of the built in templates that came with my Joomla! distro figuring that if this worked OK, I definitely had a Gantry problem. If it failed, I had a Joomla! problem. Well, the error message went away, but other than that, the results were the same. Deep in the framework, something was blowing up.

Next--ON THE DEV SYSTEM--I put a bunch of error_log() statements in the JUser::save() method to find out where it was blowing up. It was blowing up at the very end when it was trying to trigger the "onUserAfterSave" event in listeners. If I commented out that trigger, everything worked perfectly.

That wasn't the solution, but it was a useful clue.

Next, I grep-ed for every php module that had registered the "onUserAfterSave" method and did my error_log() peppering thing to see which file was causing the problem. As it turned out, there is a ContactCreator plugin that came with the Joomla! distribution and was enabled (by default? I don't remember.). Here's what that plug in does: every time you add a user to Joomla!'s tables, it creates a corresponding contact_details record and puts a variant of the user's real name in an `alias` column. Here's what that plug does NOT do: when you delete a user using the Administrator interface, it does NOT delete the contact record. Here's one more thing that it DOES do: If you try to add a record that would result in a duplicate `alias`, it blows up and tries to send an HTML error back to the browser.

Because I was authorizing the same record over and over, this was what was killing me.

My solution? I just disable that ContactCreator plug in. I don't need it or it's bad attitude.

Selah!

No comments:

Post a Comment