A more useful "on_user_add" event for Concrete 5

When you use the Concrete5 events system, you can hook in to run a block of code every time that a new user is added to the site.  However, this event is actually run before the user attributes are assigned to the new user.  So what are you supposed to do to get a full user object with all the attributes passed to your custom code?  If you'd like to know, hit the Read More link ;)

This morning tash on the IRC channel hit me up for a little help with this problem.  He'd been talking to just about all of the other developers in the channel for the last week or so, and had a forum post about it, and had found this post as well.  But he couldn't understand exactly what it was that dg1 had actually done.

Looking at it, it was pretty obvious to me.  What's happening is that the user is getting added to the system, and then the user attributes are getting assigned to the newly created user.  After all, you can't assign attributes to something that doesn't exist.

  1. // do the registration
  2. $data = $_POST;
  3. $data['uName'] = $username;
  4. $data['uPassword'] = $password;
  5. $data['uPasswordConfirm'] = $passwordConfirm;
  6.  
  7. $process = UserInfo::register($data);
  8.  
  9. if (is_object($process)) {
  10.  
  11. foreach($aks as $uak) {
  12. $uak->saveAttributeForm($process);
  13. }

What's happening here (it's around line 130 in version 5.4.1) is that the user is being added to the system on line 7.  If you look at the UserInfo::register() method, it's actually the one that fires off the "on_user_add" event.  Then, if we have a good UserInfo object returned, we continue with the rest of the registration. 

The first thing that happens once we have a good UserInfo object ($process) is that we assign the attribtes to it.  What we need to do is fire off a second event once these attributes have been assigned.

So I copied the file:

/site_root/concrete/controllers/register.php
to
/site_root/controllers/register.php

So that we can override the functionality without modifying the actual files in the Concrete core.  This also means that our customizations won't be lost in the event of an upgrade.

Once we have the file copied out, I change the file just a bit to look like this:

  1. // do the registration
  2. $data = $_POST;
  3. $data['uName'] = $username;
  4. $data['uPassword'] = $password;
  5. $data['uPasswordConfirm'] = $passwordConfirm;
  6.  
  7. $process = UserInfo::register($data);
  8.  
  9. if (is_object($process)) {
  10. // we have a new user, let's assign the attributes
  11. foreach($aks as $uak) {
  12. $uak->saveAttributeForm($process);
  13. }
  14. // now let's tell the system that we assigned the attributes and the user is good to go
  15. Events::fire('on_user_attributes_saved', $process);

So now we're going to be calling a new event "on_user_attributes_saved" right after saving the attributes, so we need to add this event to the system.  To do this, we need to look into the Events model a little bit.  The documentation on events is pretty clear.

First off, we need to make sure that we have application events enabled if we're using a version of Concrete5 that's less than 5.4 - I recommend using the latest version, but if you need this functionality in an older version, the process is the same.  For those versions, you need to add the following line to your

/config/site.php

file:

  1. define('ENABLE_APPLICATION_EVENTS', true);

After we've made sure that events are actually enabled, we need to create a file at

/site_root/config/site_events.php

And then we need to add the following to it:

  1. // syntax is like this:
  2. // Events::extend($event, $class, $method, $filename, $params = array());
  3.  
  4. // so we need ours to look like this:
  5. Events::extend('on_user_attributes_saved', 'UserInfoEvents', 'attributesSaved', 'models/user_events.php');

What we are doing here is calling a method on a class when the event happens, and specifying which file on the system it is that we're calling.  In this case, we need to create a file at

/site_root/models/user_events.php

We will put our class declaration and method inside of this file.  We could actually put other methods in here, if for instance we needed to run something on other user events like on_user_delete, on_user_update, or on_user_login.

The user_events.php file needs to look something like this:

  1. defined('C5_EXECUTE') or die(_("Access Denied."));
  2.  
  3. class UserInfoEvents extends Object {
  4.  
  5. public function attributesSaved($ui){
  6. // run your custom code here
  7. // the $ui object is the $process variable from the register.php controller
  8. // you can do anything here, send a second email to the site admin,
  9. // create a custom page for the new user, whatever
  10. }
  11. }
  12. ?>
  13.  

Now whenever a new user signs up for our website, the custom code inside of our model is run.  Because we are waiting to run this event until after the user object is completely set up with all attributes assigned, we have a full UserInfo object we can manipulate and check against.  So if you have custom registration fields, they will actually be available to this method.

blog comments powered by Disqus