Logo name

Ipb 2.1:IPB Components System Tutorial

From IpbWiki

  • Currently5.00/5
Jump to: navigation, search

Contents

Introduction

The components system is a way of adding functionality via custom modules to your board. These modules are protected from IPB core updates and as such, modification maintenance is reduced.

The components framework is a system of special directories that can accept custom coded modules. These modules are uploaded into specific directories that perform different functions.

The components system also allows one to configure settings that allow seamless integration with the admin control panel and admin control panel permission settings.

This tutorial will show you how to add a new component and the special requirements needed when writing the component modules.

We'll be using the "example" files that ship with IPB as a basis for our component.


The Modules' File Structure

Image:Components dir structure.gif

This screen capture shows the contents of the "sources" directory. The components folders are as follows:

  • components_acp: This folder accepts the modules that control any control panels in the ACP that the admin will use to modify and manage this component.
  • components_init: This folder accepts the modules that contain any initialization code the component requires, such as any caches to be loaded.
  • components_location: This folder accepts the modules that contain any code that handlers the user's online location. This is used both in the online list and the user's profile to show where they are currently active.
  • components_public: This folder accepts the modules that contain any code to handle the public facing part of the component system.
  • components_ucp: This folder accepts the modules that deals with the User Control Panel menu generation.

Adding a new component from the ACP

In most cases, you'll want to write the custom modules before adding the component to the ACP (Admin Control Panel) ­ but in order to understand what to write, how to name it and where to put it, we'll start by creating a new component in the ACP.

Log in to your ACP, and click on the "Admin" tab. Select "Menu Components" from the "Components" menu. You'll see the screen capture shown below.

Image:Manage components.gif

This screen allows you to edit the components (where allowed, some are locked by default), disable components and add new ones.

We'll add a new one. Click the menu icon next to the "Enabled" label. Choose "Add new component".

You'll see a form similar to the one in the next screen capture. We've already filled this one out for demonstration purposes. We'll go through each field, step by step. Component Title: This is, as the name suggests, the title for this component. It is used only on the main components screen in the ACP to identify this component.

Image:Register new component.gif

  • Component Title : What do you want to call your component.
  • Component Version: This is an optional field to determine the version number of the component. If you plan on releasing your component for others to use (and this is encouraged!) then you may wish to add a version number to make it clear which version is being used.
  • Component Brief Description: This is a short description about the component that is also only used when listing components on the main component page in the ACP.
  • Component Author: As the name suggests, the name of the author of the component
  • Component Home URL: Where does this component live on the internet?
  • Component ACP Menu Data: The components system allows one to add a menu dynamically in the ACP under the Components tab. This simply means you can manage your ACP menu for the component without having to edit the ACP PHP files.
    • Menu Text: This is the content of the link shown in the menu. For a link that allows one to edit settings, "Settings" may be appropriate.
    • Menu URL: This is the `custom' part of the URL. The system automatically includes the main ACP URL and unless you select to have this "Menu Redirect" to another part of the ACP, you only need to enter the "code=" part this will be picked up in the module code and the relevant function loaded. We'll cover this in greater detail later on.
    • Menu Perm Bit: This is the permission key used in the ACP Permission Restrictions Settings. We'll cover this in greater detail later on.
    • Menu Perm Bit Lang: This is the language string used in the ACP Permission Restrictions center.
  • Component Board Header URL: This is the URL to the public facing part of the component. In nearly every case, you will need to use {ipb.base_url}autocom=xxxxxx. Where xxxxxx is, is the component key that is used to name the component module files. This component key is entered in the Component Section Code field. We've used "hello". This means our component modules must be named "hello.php" and we'll use the link "autocom=hello"
  • Component Board Header Title: This is the text of the Board Header URL. When the URL and title are complete, the component link will be added automatically to the board header, next to the "Search, Calendar, Member", etc links.
  • Component Enabled: Do you wish to use this?
  • Component Section Code: Its best here to use your Component's title in lowercase and without any spaces.


Example

  • Component Title : Hello
  • Component Version: 1
  • Component Brief Description: A test example for IPB
  • Component Author: wiki
  • Component Home URL: http://www.ipbwiki.com
    • Menu Text: Setup
    • Menu URL: do=setup
  • Component Board Header URL: {ipb.base_url}autocom=hello
  • Component Board Header Title: Hello
  • Component Enabled: Yes
  • Component Section Code: hello


Here is the example one for "Hello". You can copy and paste this into a file called "ipd_hello.xml" and be uploaded!

CODE
<?xml version="1.0" encoding="ISO-8859-1"?>
<componentexport exported="1205004060">
<componentgroup>
 <component>
  <com_id>8</com_id>
  <com_title>Hello</com_title>
  <com_author>wiki</com_author>
  <com_url>http://www.ipbwiki.com</com_url>
  <com_version>1</com_version>
  <com_date_added>1205000792</com_date_added>
  <com_menu_data><![CDATA[a:1:{i:1;a:5:{s:9:"menu_text";s:5:"Setup";s:8:"menu_url";s:8:"do=setup";s:13:"menu_redirect";i:0;s:12:"menu_permbit";s:0:"";s:13:"menu_permlang";s:0:"";}}]]></com_menu_data>
  <com_enabled>1</com_enabled>
  <com_safemode>0</com_safemode>
  <com_section>hello</com_section>
  <com_filename>hello</com_filename>
  <com_description>A test example for IPB</com_description>
  <com_url_title>Hello</com_url_title>
  <com_url_uri>{ipb.base_url}autocom=hello</com_url_uri>
  <com_position>10</com_position>
 </component>
</componentgroup>

</componentexport>

Module: components_acp

We'll start by writing our ACP module. As mentioned previously, this must be named inline with our chosen component section code. In our case, we've chosen "hello" ­ so name this file "hello.php". See the example files for a complete code listing.

Firstly, we need to name the class inline with our section code.

CODE
class ad_hello
{
     //Global
    var $ipsclass;

We've used "ad_hello" as this is the name of the class.

Once this class has been loaded, IPB will look for a function called "auto_run" In this module we put all our work logic.

CODE
function auto_run()
{
    switch( $this->ipsclass->input['do'] )
    {
         case 'setup':
              $this->show_setup();
              break;
    }
}

Notice we do a "switch" on the `code' URL bit? In our menu data, we chose "code=setup" as our URL bit. We need the module to direct that to the correct function ­ in our case we send this request to our "show_setup" function.

CODE
function show_setup()
{
    $this->ipsclass->html = "Welcome, there's not much here - that's up to you to code!";
    $this->ipsclass->admin->output();
}

Naturally, this isn't the most exciting function ever written. It simply displays a message confirming that we've access the correct part of the module. $this->ipsclass->admin->output() prints the ACP and it's very important that you have that as the last part of the function. Modules: components_init

Next up, we'll ask IPB to load a custom cache that we've stored in the "cache_store" table. You can add any other initialization work here, including loading other modules, getting information from the database, etc.

This class name is simply "component_init"

CODE
//-----------------------------------------
// This must always be 'component_init'
//-----------------------------------------

class component_init
{
    var $ipsclass;

    /*-------------------------------------------------------------------------*/
    // run_init
    // Do any work before the caches are loaded.
    // ADD to $this->ipsclass->cache_array()
    // DO NOT overwrite it or call $this->ipsclass->cache_array = array(...);
    // As the array has already been started off by IPB in index.php
    /*-------------------------------------------------------------------------*/

     function run_init()
     {
          $this->ipsclass->cache_array[] = 'hello';
     }
}

The function loaded MUST be called "run_init". We want to load the 'hello' cache when IPB loads all the other caches (forums, settings, groups, etc) so we add 'hello' to the cache array.


Here is the complete code for "./sources/components_acp/hello.php"

CODE
<?php
class ad_hello
{
// Global
var $ipsclass;

function auto_run()
{
 switch( $this->ipsclass->input['do'] )
 {
  case 'setup':
  $this->show_setup();
  break;
 }
}

function show_setup()
{
 $this->ipsclass->html = "Welcome, there's not much here - that's up to you to code!";
 $this->ipsclass->admin->output();
}
}

//-----------------------------------------
// This must always be 'component_init'
//-----------------------------------------
class component_init
{
var $ipsclass;

/*-------------------------------------------------------------------------*/
// run_init
// Do any work before the caches are loaded.
// ADD to $this->ipsclass->cache_array()
// DO NOT overwrite it or call $this->ipsclass->cache_array = array(...);
// As the array has already been started off by IPB in index.php
/*-------------------------------------------------------------------------*/

function run_init()
{
 $this->ipsclass->cache_array[] = 'hello';
}
}

?>

Module: components_init

This folder accepts the modules that contain any initialization code the component requires, such as any caches to be loaded.

  • coming soon*

Module: component_location

This file is loaded by IPB when a user views the online list or profile page and another user is active in that component.

This class must be named: components_location_XXXXXX. Where XXXXXX is, replace with your component section code.

CODE
class components_location_hello
{
    var $ipsclass;

The first function "get_session_variables" returns information about the user's current session. If they access the component from the public facing part, you can pass back to IPB information on their location. There are three ID slots available.

CODE
/*-------------------------------------------------------------------------*/
// get_session_variables
// Returns:
// array( '1_type' => {location type #1} [ char(10) ]
//         '1_id'   => {location ID #1}   [ int(10) ]
//         '2_type' => {location type #2} [ char(10) ]
//         '2_id'   => {location ID #2}   [ int(10) ]
//        '3_type' => {location type #3} [ char(10) ]
//         '3_id'   => {location ID #3}   [ int(10) ]
//      );
// All are optional.
// Use this to populate the 'module_id_*' fields in the session table
// so you can check in your own scripts it the member is active in your module
/*-------------------------------------------------------------------------*/

function get_session_variables()
{
    return array( '1_type' => 'hello',
                  '1_id'   => 1 );
}

In our example, we'll return 'hello' as the location type, and a generic ID of `1'. This can be anything ­ a page ID, a forum ID, a unique ID ­ it depends on the module.

The second function "parse_online_entries" takes the raw session entries and formats them depending on the information contained.

CODE
/*-----------------------------------------------------------------*/
// parse_online_entries
// INPUT: $array IS:
// $array[ $session_id ] = $session_array;
// Session array is DB row from ibf_sessions
// EXPECTED RETURN ------------------------------------
// $array[ $session_id ]['_parsed'] = 1;
// $array[ $session_id ]['_url']    = {Location url}
// $array[ $session_id ]['_text']   = {Location text}
// $array[ $session_id ] = $session_array...
//
// YOU ARE RESPONSIBLE FOR PERMISSION CHECKS. IF THE MEMBER DOESN'T
// HAVE PERMISSION RETURN '_url'    => $this->ipsclass->base_url,
//                             '_text'   => $this->ipsclass->lang['board_index'],
//                             '_parsed' => 1 { as well as the rest of $session_array }
/*-----------------------------------------------------------------*/

function parse_online_entries( $array=array() )
{
    $return = array();

     //-----------------------------------------
     // LOOP
     //-----------------------------------------

    if ( is_array( $array ) and count( $array ) )
    {
         foreach( $array as $session_id => $session_array )
         {
              if ( $session_array['location_1_type'] == 'hello' )
              {
                   $location = $this->ipsclass->base_url.'autocom=hello';
                   $text     = "Reading the hello message";
              }
              else
              {
                   $location = $this->ipsclass->base_url;
                   $text     = $this->ipsclass->lang['board_index'];
              }

             $return[ $session_id ] = array_merge( $session_array, array( '_url' => $location, '_text' => $text, '_parsed' => 1 ) );
         }
    }

     return $return;
}

We look for 'hello' in `location_1_type' ­ as we set it in the first function. We could also check for the ID and change the online line appropriately. Modules: component_public

The "components_public" module shows the public facing component when accessed via the "autocom=xxxx" URL bit.

The class name MUST be "component_public"

CODE
class component_public
{
    /**
    * IPSclass object
    *
    * @var object
    */
    var $ipsclass;

IPB looks for a function called "run_component". We're just going to print a very simple message.

CODE
/**
* Main function that's run from index.php
*
*/
function run_component()
{
    print "hello";
    exit();
}


Here is the complete code for "./source/component_location/hello.php" (untested)

CODE

<?php

class components_location_hello
{
   var $ipsclass;

/*-------------------------------------------------------------------------*/
// get_session_variables
// Returns:
// array( '1_type' => {location type #1} [ char(10) ]
//         '1_id'   => {location ID #1}   [ int(10) ]
//         '2_type' => {location type #2} [ char(10) ]
//         '2_id'   => {location ID #2}   [ int(10) ]
//        '3_type' => {location type #3} [ char(10) ]
//         '3_id'   => {location ID #3}   [ int(10) ]
//      );
// All are optional.
// Use this to populate the 'module_id_*' fields in the session table
// so you can check in your own scripts it the member is active in your module
/*-------------------------------------------------------------------------*/

function get_session_variables()
{
    return array( '1_type' => 'hello',
                  '1_id'   => 1 );
}

/*-----------------------------------------------------------------*/
// parse_online_entries
// INPUT: $array IS:
// $array[ $session_id ] = $session_array;
// Session array is DB row from ibf_sessions
// EXPECTED RETURN ------------------------------------
// $array[ $session_id ]['_parsed'] = 1;
// $array[ $session_id ]['_url']    = {Location url}
// $array[ $session_id ]['_text']   = {Location text}
// $array[ $session_id ] = $session_array...
//
// YOU ARE RESPONSIBLE FOR PERMISSION CHECKS. IF THE MEMBER DOESN'T
// HAVE PERMISSION RETURN '_url'    => $this->ipsclass->base_url,
//                             '_text'   => $this->ipsclass->lang['board_index'],
//                             '_parsed' => 1 { as well as the rest of $session_array }
/*-----------------------------------------------------------------*/

function parse_online_entries( $array=array() )
{
    $return = array();

     //-----------------------------------------
     // LOOP
     //-----------------------------------------

    if ( is_array( $array ) and count( $array ) )
    {
         foreach( $array as $session_id => $session_array )
         {
              if ( $session_array['location_1_type'] == 'hello' )
              {
                   $location = $this->ipsclass->base_url.'autocom=hello';
                   $text     = "Reading the hello message";
              }
              else
              {
                   $location = $this->ipsclass->base_url;
                   $text     = $this->ipsclass->lang['board_index'];
              }
             $return[ $session_id ] = array_merge( $session_array,array( '_url' => $location, '_text' => $text, '_parsed' => 1 ) );
         }
    }

     return $return;
}
}

class component_public
{
  /**
    * IPSclass object
    *
    * @var object
    */
    var $ipsclass;
 
 /**
 * Main function that's run from index.php
 *
 */
function run_component()
{
    print "hello";
    exit();
}
}
?>

Module: components_ucp

This file deals with building the UCP ( User Control Panel) menu. This allows one to dynamically manage the UCP menu without having to make hard edits to the IPB PHP sources.

We could, for example, build a function into the public facing component module to allow the user to edit the settings. Our test module won't have this functionality, but we'll cover the operation nonetheless.

This class MUST be called: components_ucp_XXXXX. Where XXXXX is the name of the section component code.

CODE

class components_ucp_hello
{
//IPB looks for the function "ucp_build_menu".
function ucp_build_menu()
{
      //-----------------------------------------
      // INIT
      //-----------------------------------------

     $content = "";

     //-----------------------------------------
     // Get links
     //-----------------------------------------

    $content .= $this->ipsclass->compiled_templates['skin_ucp']->menu_bar_new_link( "{$this->ipsclass->base_url}autocom=hello&amp;code=ucp_settings","User Settings" );

    if ( $content )
    {
         return $this->ipsclass->compiled_templates['skin_ucp']->menu_bar_new_category( "Hello", $content );
    }
    else
    {
         return '';
    }

}
}

We use the built in functions to generate the menu. First a menu link "User Settings", which we pass to be wrapped in a category called "Hello".

Admin Restriction Permissions

As you may recall earlier, when creating this new component in the ACP (Admin Control Panel) we chose a “perm bit” and a “perm lang”. We chose the perm bit “setup” and the perm lang “Allow ACCESS to set up”.

This will be used in the Admin Restriction Permissions screen to allow the admin enable or disable access to that menu link.

This is how it looks in the restrictions screen:

Image:Acp restriction permissions.gif

Conclusion

You have now created a very basic components system that has a custom cache loaded, custom online list locations, custom UCP menus, custom ACP menus and custom public and ACP facing output.

(Article converted from pdf in official Ipb documentation)

This page was last modified on 8 March 2008, at 19:35.  This page has been accessed 6,938 times.  Content is available under GNU Free Documentation License 1.2Disclaimers