Integration of a bbPress forum into a cakePHP application (part 3)

Let's go back to bbPress now. With the previous post, you should be able to create bbPress user directly from your app.

Redirecting bbPress to the cakePHP app actions

What we'll be doing now is adding some hooks on every user-related action on bbPress to redirect them to their equivalent in your app. No need to have two different login form, hey ?

Create a new php file in your app/webroot/forum/my-plugins/ directory, and copy and paste the following code :

/*
Plugin Name: cakePHP User
Description:  Remaps each login/register/forgotpass action to those of your own application
Version: 0.1
Author: Pixelastic
Author URI: http://www.pixelastic.com/

License: CC-GNU-GPL http://creativecommons.org/licenses/GPL/2.0/

*/

define( 'APP_URL', 'http://www.myapp.com/' );
/**
 *    __convertToAppUrl
 *    Convert a given bbPress URI to one mapped to the main application
 **/
function cake_convertToAppUrl($uri) {
   // We explode the path in different subdirs
   $paths = explode('/', $uri);
   $nbrPath = count($paths);

   // We get the last part of the url
   $endUri = substr($uri, strrpos($uri, "/")+1);

   $mappedUrl = array(
     // Register
     'register.php' => 'users/add',
     // Lost password
     'password-reset.php' => 'users/pass',
     // Login
     'bb-login.php' => 'users/login',
     // Logout
     'bb-login.php?action=logout' => 'users/logout',
     // Profile editing
     'edit' => 'users/edit/'.$paths[$nbrPath-2]
   );

   // If there is a mapping, we return it, otherwise we don't touch the uri
   return (!empty($mappedUrl[$endUri])) ? APP_URL.$mappedUrl[$endUri] : $uri;
}
// We filter both bb_uri and bb_get_uri
add_filter('bb_uri', 'cake_convertToAppUrl', 1);
add_filter('bb_get_uri', 'cake_convertToAppUrl', 1);

It will hook every method that ask for a url and convert the result to return the corresponding url in your own app.

Don't forget to enable this plugin in your bbPress admin panel.

Of course, feel free to change the destination url to reflect those of your app.

Additional information

Note that theusers/edit/method takes the bbPress nicenameas argument, so you should have to convert it before displaying the edit form.

I also edited my template and added an hidden field in the bbPress login form to check if the login was coming from the app or the forum and redirect accordingly.

Integration of a bbPress forum into a cakePHP application (part 2)

We've done enough work on the forum for now. The time has come to edit our cake files.

Creating the cakePHP models

We will create two models to help in speaking with the bbPress database.

Create a bb_usermeta.php model :

class BbUsermeta extends AppModel {
  var $useTable = 'bb_usermeta';
  var $primaryKey = 'umeta_id';
}

Create a bb_user.php model :

class BbUser extends AppModel {
  var $primaryKey = 'ID';

  var $hasMany = array(
     'BbUsermeta' => array(
       'className' => 'BbUsermeta',
       'foreignKey' => 'user_id',
       'dependent' => true
    )
   );

  //We make sure, before saving, that the nicename is url-friendly
  function beforeSave() {
    if (!empty($this->data[$this->name]['user_nicename'])) {
      $this->data[$this->name]['user_nicename'] = Inflector::slug($this->data[$this->name]['user_nicename']);
    }
    return true;
  }

}

So, what was that about ?

We created two models to communicate with bbPress. bbPress stores user-related information in bb_users as well as bb_usermeta. We defined the $primaryKey, $useTable and $foreignKey of the $hasMany because the bbPress tables do not follow the cakePHP convention.

We also add a nifty beforeSave() method to BbUserto make sure its user_nicename (used as an url slug) is url-friendly.

How to save bbPress users from the cakePHP app

Great. Now you can easily add, edit and delete users from bbPress directly from your app.

There are some things you should know about before doing that :

  • first_name and last_name are stored in bb_usermeta
  • the access rights are defined in bb_usermeta too, using the bb_capabilities key

Now that you know all that, you can easily create a behavior creating a new bbPress user when creating a new user, deleting that bbPress user when deleting the main user and updating the bbPress user when updating the main user.

I myself wrote this behavior, but as it is part of a more general bbPress plugin it may not work as-is. I'll try to publish it if anyone is interested.

Integration of a bbPress forum into a cakePHP application (part 1)

Today I had to integrate a forum into an existing cakePHP app. I didn't want to code the whole thing from scratch and wanted to take advantage of one of the great opensource forums out there instead.

I wanted to keep the forum as a stand-alone part, with its own admin panel, and not mess with its core files, to allow me the possibility of updating it if needed. But I didn't want my user to register twice, for my app and for the forum, I wanted the whole register/login thing to be as smooth as possible.

After some trials, I finally choose bbPress, from the makers of WordPress.

I took me some time, digging into the online documentation (pretty much non- existent), digging into existing plugins and the source code to finally script what I needed. I had to code a cake behavior, a cake component, a bbPress plugin, overwriting some bbPress functions but it finally works.

Installing bbPress on a cakePHP app

First thing you need to do is installing bbPress. Just drop the files you downloaded into app/webroot/forum and follow the installation instruction. Set the admin login the same as your own admin login in your app. Type anything you want for the password, we'll change that later.

Once this is done, you should have a working forum, and being able to access it on /forum. It's a good start, isn't it ?

Now, open your favorite mysql editor (Navicat or phpmyadmin) and connect to your database. Make sure the collation of the bb_ tables are the same as the collation of your cake tables. It will allow them to correctly communicate. My cake tables were set to utf8_unicode_ci and my bbPress tables were set to utf8_general_ci. I had to update all the bbPress tables to make sure no collation problems arise.

Using the same password hashing

Now let's look at our bb_users table. You should have one entry for your admin account. Go to your own cake userstable, copy your password hash and paste it in place if the bbPress one. Cake and bbPress do not use the same hashing algorithm, so we are going to change that.

Open forum/bb-config.php and add the following code :

define('CAKE_SALT', "XXXXX");
// Check password using sha1
function bb_check_password( $password, $hash, $user_id = '' ) {
  // Does it match ?
  return sha1(CAKE_SALT.$password)==$hash;
}

Of course, replace the XXXXXvalue of CAKE_SALT with your own Security.Salt value (usually found in app/config/core.php). Also note that the default hashing method used by cake is sha1, but if you use an alternate md5or sha256, you'll have to update the method above.

Doing all this, we change the way bbPress checks for a valid password. The method gets the plain password and the hashed version stored in the database and compare them. Cake does its comparison using its own internal salt value and applying sha1() on it. We just do the same here. That way, we can now login using the same password.

Reading my own code from 3 years ago

Today I had to update an old site I developped some years ago. I had to move it from one host to another and make sure all the configuration followed.

It scares me. What I wrote 3 years ago scares me. My code was... well there was some huge mistakes, things I would never ever do again. Today, I would severly frown upon any coder who wrote horribles things like that.

For the php part, here is a very good list of what NOT to do :

  • I used shorthand notation for my php tags : <? instead of <?php.
  • My server was set as register_global off, but I was circumventing it by doing my own register_global on :
extract($_SERVER);
extract($_COOKIE);
extract($_GET);
extract($_POST);
  • My files were encoded in Latin-1, my database was in latin1_swedish_ci and my html head was set to iso-8859-1. At least, that was consistent.
  • I wasn't EVER testing if my variables were set, so I had notices popping everywhere. I had the error_reporting set to disable them, hiding the error instead of writing better code.
  • The password of my users were stored UNCRYPTED in the database... And stored uncrypted in the cookie as well.
  • I was doing something that was supposed to be OOP, but I never used heritage, so all my classes were containing the same methods, copied and pasted from one file to another, and changing only some variable names...

And some other mistakes :

  • I included a whole bunch of Javascript files (like 15 or so) and was NOT using any js framework. Well, technically I was using a js framework, but one I developped. And let be honest, I can't really call it a framework today.
  • I used letter-spacing:-1000em to hide text (when using an image as a header for example). It broke on Firefox, I'm now using text-indent:-1000em; (really, I don't know if this is better).
  • There was a flash animation with sound, enabled by default and with auto-play. For my defense, I remember having told by ex boss that it was a bad idea but he insisted.

To be honest a part of me is horrified of what I wrote some years ago and thought was great. On the other hand, I'm quite proud of the progress I have made in so little time.

DNS caching and flushing

Yesterday I moved this domain from one server to another on my Dreamhost account (actually, I moved the whole domain from one Dreamhost account to another, but that's the same).

The DNS saved at the registrar where the same (ns1.dreamhost.com, ns2.dreamhost.com and ns3.dreamhost.com). The transfer went seamlessly and I was able to access my website on the new server in an invisible way.

But, strangely, some hours laters I started to experience slow down when accessing the website and finaly all my requests timed out, be it either from Firefox or a direct ping.

I tried with my windows xp and my Ubuntu 8.04. I even change the DNS on xp to use opendns, run an ipconfig /flushdns, rebooted. On Ubuntu I've done a /etc/init.d/dns-clean start. I even rebooted my adsl box, thinking there was something wrong with the DNS cache on my side.

I even contacted both Dreamhost and Gandi with a complete traceroute to know if they could help me, as I thought having done everything that I could think of to resolve the issue. They assure me that their configuration was fine and the problem wasn't on their side.

When asking friends, or testing with http://downforeveryoneorjustme.com/ it occured that the problem really was on my side.

Everyone were true. The problem lied somewhere in between, in the DNS cache hold by my ISP that wasn't correctly up to date. I don't really understand how that could happen, I thought rebooting my Freebox would clear its cache, and specifying dns addresses of opendns would bypass the dns resolve of my ISP but it seems not to be the case.

Anyway, after waiting for some more hours, my website was again reachable through sshand ftp.

So, if you're sure your dns configuration is correct both on your register and on your hosting service, make sure to clear every DNS cache you can have on your own side. That include windows inner dns cache, those of your browser, those of your router, and those of your specified preferred DNS resolving server.

If all that failed, it should be caching issue with your ISP, and there is nothing you can do about that, except waiting.