MyBB Community Forums

Full Version: Let's plugin it!!
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
Pages: 1 2 3
Hello guys,

Previously i made the tutorial Common Errors when making a plugin ; however this seems not to be enough and i was asked several times to make a full guide about coding MyBB plugins.

The tutorial will cover the following:
  1. The concept.
  2. Building the idea.
  3. Feasibilty study.
  4. Preparation.
  5. Make it MyBB's.
  6. Main structure of plugins.
  7. Hooks.
  8. Notes to be considered.
  9. Functions inside the plugin file.
  10. Editing templates.
  11. Editing other php files.
  12. Adding settings through the plugin file.
  13. Overview

The codes here do not work with versions ealier then 1.2.x.

The concept
The concept of the plugin is to keep hand work away from end-users, the plugin file should do everything for you as an end-user and you are not supposed to go and type or copy/paste any code from a readme file or something like this.

Building the idea
Coming up with any idea isn't something easy, but you should always remember to do what is demanded. Find those things will benifit anyone and that anyone can use or want.

Feasibility study
This is of course at some point to the idea & strictly related to the concept. If the idea you have will lead complicated plugin that doesn't work by it self or that needs a lot of preparation from the user and probably will not function as expected, then ditch your plan.

Preparation
Of course MyBB plugin files are php files. Therefore your file should be saved with .php extension.
Here an important part you should take care of so you can continue with your plugin, it is the name of your file, the name of your file is needed in all the main functions of a plugin later on.

Let's name our file now myplugin.php, and for now it has only
<?php 

?>
 

Make it MyBB's
Any file uploaded to ./inc/plugins will not function, like a strange corp added to a body. That's why you need at the start of your plugin to have the following

if(!defined("IN_MYBB"))
{
    die("Direct initialization of this file is not allowed.<br /><br />Please make sure IN_MYBB is defined.");
}

Main structure of plugins
Here we come to the coding part. Refering back to the concept which stats that the plugin should do the work by it self, then we know that we have a step where we enable and disable the plugin.
During those 2 steps adding and removing takes place. In addition a plugin should have information about it. The the main 3 structures are as follows:

//All the information about the plugin. These will appear in the plugins list.
function myplugin_info()
{
    return array(
        "name"          => "My plugin",
        "description"   => "This is my 1st plugin",
        "website"       => "http://www.thetechex.net",
        "author"        => "Zaher1988",
        "authorsite"    => "http://www.thetechex.net",
        "version"       => "1.0",
    );
}

//All the activation processes go here
function myplugin_activate()
{

}

//All deactivation processes go here
function myplugin_deactivate()
{

}
As you can notice, what i have refered to previously regarding the file name show up here. The plugin name is always the prefix of those main functions.

Through out the plugin we will add our codes to the activation and deactivation functions.

Hooks
Hooks were made especially for plugins, instead of having the code placed in the remote file, hook simply work them out as they are there, however they still in the plugin file.

There are 2 types og hooks Hooks without arguments and Hooks with arguments.

$plugins->add_hook("hook_name", "function_name");

The hook_name is the name of the hook which you can find when a hook is added to a certain file, list of all hooks can be found here. The function_name is any function that you will create later on in the file and that should be loaded in that hooked file.

Remember you can have more than one hooks, and you may use the same function in different hooks.

Notes to be considered
Before we continue with functions and template changing etc.. we need to know something about the calling of other functions and classes, and this is not only for MyBB plugin, it is a basic in coding functions.

When ever you are coding in a function, and you call an external variable you need to set it under global;

global $my_external_var;
So assume while coding a MyBB plugin function, i needed to use on of MyBB classes. there for we should do the following

 
function myfunction()
{
global $db;

$new_var = $db->escape_string('<td class="thead"><strong>Hello fella</strong></td>');
}

As you can see we needed $db as a global because we are using a MyBB class with $db.

Another need for this in MyBB plugins is for example when using hooks, assume your plugin function is hooked to some part of MyBB and some of the variables you are trying to use in your new functions exists in that file above the hook line, what do you do to get it? you include it as a global.

function myfunction()
{
global $db, $message, $text;

$new_var = $db->escape_string($text);
$message['message'] .= $new_var;
}
You might notice that external arrays are also set this way.

Functions inside the plugin file
MyBB plugins system uses functions as any other php file, the function can have any name, and they are mostly used for hooks.

In a function like this, you can type the you want, and perform any task you want. And by including it in a hook, it will work as been placed in that other file.

It is preferable to also have the prefix same as the acitvate and deactivate functions, jut not to fall in a clash of functions where yours and one from the other file might have the same name.

As mentioned above, you can retrieve variables from outside the function, perform any action on them then return them or display them.

Let's run a function hooked to the postbit that will display a message in every postbit message, this function will be added to the previous codes and our plugin file should look now this way

<?php
if(!defined("IN_MYBB"))
{
    die("Direct initialization of this file is not allowed.<br /><br />Please make sure IN_MYBB is defined.");
}
//The 1st hook to the postbit.
$plugins->add_hook("postbit", "me");

//All the information about the plugin. These will appear in the plugins list.
function myplugin_info()
{
    return array(
        "name"          => "My plugin",
        "description"   => "This is my 1st plugin",
        "website"       => "http://www.thetechex.net",
        "author"        => "Zaher1988",
        "authorsite"    => "http://www.thetechex.net",
        "version"       => "1.0",
    );
}

//All the activation processes go here
function myplugin_activate()
{

}

//All deactivation processes go here
function myplugin_deactivate()
{

}

function me(&$post)
{
	$post['message'] .= "<br />I always edit your posts immediately :P";
}
?>

We have added &$post to the function parameters so that we can get it from outside the function.

Another example of a function is the following, we will use some mybb classes in it, let's edit the existing me();

function me(&$post)
{
	global $mybb;
	$post['message'] .= "<br />{$mybb->settings['bbname']}";
}

This way we have used the settings of MyBB in our function. You don't have only to output it, you can use them in If statements etc...
As you might notice we have added $mybb, without this $mybb->settings['bbname'] will come up empty.

You may also uses function inside other functions is the plugin file, where one function will return something to the other function.

function me(&$post)
{
	$value = giveme();
	$post['message'] .= "<br />$value";
}

function giveme()
{
	return rand();
}

Take note that the functions can also return something to the file they are hooked with.

Editing templates
Editing templates usually is done using find_replace_templatesets function, this for some represents torture and the most annoying part in coding plugins because many time you try to replace something but it doesn't work. But hopefully after this process will go smooth with you.

Working with templates isn't only modifying existing templates, but aslo adding new templates. These tasks also take place in activating and deactivation and they are somehow differently coded in each part.

Let's modify an existing template.
First of all in order to use the find_replace_templatesets you need to include some file, so in both the activation and deactivation function add the following

require MYBB_ROOT.'/inc/adminfunctions_templates.php';

Let's for example modify the header template to add something to it, and also code its removal during deactivation. The following code will show how our activate and deactivate functions should look like now:

//All the activation processes go here
function myplugin_activate()
{

 	require MYBB_ROOT.'/inc/adminfunctions_templates.php';
    find_replace_templatesets("header", '#'.preg_quote('{$welcomeblock}').'#' , '{$welcomeblock} <br /> it\'s me "right?"');

}

//All deactivation processes go here
function myplugin_deactivate()
{

	require MYBB_ROOT.'/inc/adminfunctions_templates.php';
    find_replace_templatesets("header", '#'.preg_quote('{$welcomeblock} <br /> it\'s me "right?"').'#' , '{$welcomeblock}',0);

}

header obviously is the name of the template to be edited, the second parameter is the search bit, the 3rd parameter is the replacement bit.

Note that there is no need to escape variables (\$var) or double quotations (\"hey\") when using preg_quote the way mentioned in my code. However you always have to quote the single quotes (\').
It is the same case in both search and replacement parameters.

If you look at the deactivation function, it is somehow the same, however look at the 0 at the end. You dont incluse "0" at the end of the activation for a certain change of templates. As 0 means you don't want to create a new template. however if you change something, you must create a new template otherwise reverting to original won't be possible that's why this is only found in deactivation, and without the template will not be edited according your criteria in the deactivation function.

When working with modifying templates i usually prefer to stick on one line not multi lines of search criteria at once; however if you had to it is also preferable to use s after the #, example

 find_replace_templatesets("header", '#'.preg_quote('{$welcomeblock} <br /> it\'s 
me 
"right?"').'#s' , '{$welcomeblock}',0);


Moreover it is very essential to strictly define a unique term to be search, like dont simply put a piece of code that can be found several times at the same time in the same template, put something strict to the point and very differenciated.

If you face a problem with this function where what you want to replace isn't getting replaced then go back check the search term define it more, let it be 100% unique and easy to search.

Some times, while deactivating or even activating you will need to work each line of codes by it self, this is rare however.

Let's add a new template.
Sometimes you need to add new templates through a plugin, templates of your own coding and that are needed for your plugin.

Again you have to add them then remove them, using 2 different codes, so here is an example

//All the activation processes go here
function myplugin_activate()
{
	global $db;

	$new_template = array(
		"sid"			=> "NULL",
		"title"			=> "mynew",
		"template"		=> $db->escape_string('<div align="center"><strong>Thi is my new template called called {$mybb->settings[\'bbname\']}</strong></div>'),
		"sid"			=> "-1",
		"version"		=> "100.07",
		"status"		=> "0",
		"dateline"		=> time(),
	);	
	$db->insert_query(TABLE_PREFIX."templates", $new_template);
}

//All deactivation processes go here
function myplugin_deactivate()
{
	global $db;

	$db->query("DELETE FROM ".TABLE_PREFIX."templates WHERE title ='mynew'");
}

As you can notice in the activation, we have used an array to hold the info of the new template, it explains it self actually, usually you don't have to edit anything but the template and title stick on the code provided above.

It is very essential to keep the $db->escape_string() when adding the templates HTML codes, or rather the plugin file will break while activating. Also here you don't have to escape any variable or doube quotations however you need to escape single quotations.

-1 here is used to set this template as global, this is crucial to have this template available for the rest of the template sets.

Now during deactivation, as you can see it is simply a delete query where the template title is used in the WHERE clause.

If you notice in both we have used global $db;, and this is as mentioned earlier in this tutorial needed when using MyBB classes and variable, in this case we're using query and such, ex. $db->query();


Editing other php files
Sometimes you need to edit one of MyBB files in order to get your plugin file working.

Here as an example i will show how to edit the index.php and add or replace something in it. In the example i will add a code to include some file to the index.

This function works somehow the same as the find replace function of editing templates, you need a search and replace terms.

Again 2 tasks should be done one during activation and one during deactivation, however in this case we don't use different codes such like what happens when we add templates, we just use the same codes however the search and replace criteria are swapped.

//All the activation processes go here
function myplugin_activate()
{
	//Change something in a 777 chmoded file.
	$fh = fopen(MYBB_ROOT.'/index.php', "r") or cperror("Could not open file!"); //OPEN FILE
	$data = fread($fh, filesize(MYBB_ROOT.'/index.php')) or cperror("Could not read file!"); //MAKE TEMPORARY STRING
	fclose($fh); //CLOSE FILE AGAIN
	$newdata = preg_replace('#'.preg_quote('require_once "./global.php";').'#','require_once "./global.php";
require_once "./somefile.php";',$data); //REPLACE IN STRING
	$fw = fopen(MYBB_ROOT.'/index.php', "w") or cperror('Could not open file!'); //OPEN FILE AGAIN
	$fb = fwrite($fw, $newdata) or cperror('Could not write to file'); //WRITE STRING TO FILE
	fclose($fw); //CLOSE FILE AGAIN
}

//All deactivation processes go here
function myplugin_deactivate()
{
	//Change something in a 777 chmoded file.
	$fh = fopen(MYBB_ROOT.'/index.php', "r") or cperror("Could not open file!"); //OPEN FILE
	$data = fread($fh, filesize(MYBB_ROOT.'/index.php')) or cperror("Could not read file!"); //MAKE TEMPORARY STRING
	fclose($fh); //CLOSE FILE AGAIN
	$newdata = preg_replace('#'.preg_quote('require_once "./global.php";
require_once "./somefile.php";').'#','require_once "./global.php";',$data); //REPLACE IN STRING
	$fw = fopen(MYBB_ROOT.'/index.php', "w") or cperror('Could not open file!'); //OPEN FILE AGAIN
	$fb = fwrite($fw, $newdata) or cperror('Could not write to file'); //WRITE STRING TO FILE
	fclose($fw); //CLOSE FILE AGAIN
}

As you can see, we specified the file, in 3 different locations, this file of course should be chmoded to 777 before activating or deactivating.

Here we have also used preg_quote so as the templates part, you don't need to escape anything but single quotations.

Adding settings through the plugin file
Sometimes you need to add custom settings for your new plugin. Settings usually have the settings groups and several settings belonging to this group.

So creating new settings 1st goes in the stage of adding the group, then adding the settings that belongs to it.

As MyBB retrieve the settings from the settings file under ./inc it is not enough to add your settings to the db, you have also to rebuild this settings file.

The example below will add a settings group and one setting belonging to it, then remove them on deactivation, it will also rebuild the settings file.

//All the activation processes go here
function myplugin_activate()
{
	global $db;

	 $group = array(
        "gid"            => "NULL",
        "title"          => "My plugin",
        "name"           => "myplugin",
        "description"    => "Settings for my plugin.",
        "disporder"      => "1",
        "isdefault"      => "no",
    );
    
    $db->insert_query(TABLE_PREFIX."settinggroups", $group);
    $gid = $db->insert_id(); //This will get the id of the just added record in the db
    
    
    $setting_1 = array(
        "sid"            => "NULL",
        "name"           => "mynew_setting",
        "title"          => "My new setting",
        "description"    => "Did you know how this goes? don\'t say no",
        "optionscode"    => "yesno",
        "value"          => 'yes',
        "disporder"      => '1',
        "gid"            => intval($gid),
    );

	$db->insert_query(TABLE_PREFIX."settings", $setting_1);
	rebuildsettings();
}

//All deactivation processes go here
function myplugin_deactivate()
{
	global $db;

    $db->query("DELETE FROM ".TABLE_PREFIX."settings WHERE name='mynew_setting'");
    $db->query("DELETE FROM ".TABLE_PREFIX."settinggroups WHERE name='myplugin'");
    rebuildsettings();
}

As you can see, in the activation part we have 1st setup a group, this will show up in the settings list.

The $gid = $db->insert_id(); is very vital for the adding the settings belonging to this settings group, this will get the id for the row that was just added to the db, we will use it when adding the settings as you can see below it.

Now look at $settings_1, this is a sample for a setting, the parts i will illustrate on the most are the description, optionscode and value. In description pay attention on escaping the single quotes. optionscode are probably the most important part here, this specify the way the setting is set, whether a radio button with yes/no or a text input etc... the following list explain what to use for each:
  • yesno for Yes/No radio button.
  • text for text input field.
  • textarea for the text area field.
  • for the drop-down menu you need a different work as follows
    Quote:select
    setting_real_value=Setting name
    verify=Send Email Verification
    and in the value you will have here to name what you are doing, for example reg_type.

In value, you may enter anything, or keep it blank, you may also enter html codes but always remember to real escape them using $db->escape_string, and always escape single quotations.

Now let's look at the deactivation of settings, again it is a simple deleting query which is ran delete an entry according to the name of the settings, so the name you have used in adding the settings will be the name in the where clause in this case.

You might notice having rebuildsettings(); in both functions, this function is responsible for rebuilding the settings file for the reason mentioned earlier.

Overview
So we have seen all the aspects of doing a plugin, we have passed oever all the possible things you might need to do, now all you have to do is to copy these codes, modify them to suit your needs, and run your plugin.
For the function names, I'd recommend also prefixing your custom function's name with your plugin filename so instead of just me() it will be myplugin_me(). This helps avoid function name conflicts among different plugins that might use the same function name.
yes you are right actually..
You forgot some ; after some lines. And the function in MyBB is rebuildsettings. Not rebuild_settings and is not needed at all really.
CraKteR Wrote:You forgot some ; after some lines. And the function in MyBB is rebuildsettings. Not rebuild_settings and is not needed at all really.

The ; were fixed.

How come it is not needed?? you need to rebuild the settings file.
Thanks... you made it really quick!
LOL i just found myself typing it todayBig Grin
zaher1988 Wrote:
CraKteR Wrote:You forgot some ; after some lines. And the function in MyBB is rebuildsettings. Not rebuild_settings and is not needed at all really.

The ; were fixed.

How come it is not needed?? you need to rebuild the settings file.

I think crakter is saying you don't need to re define it. rebuildsettings() already exists in functions.php

Also function names in a plugin file need to be prefixed with the filename excluding the .php extension. This is because multiple functions could clash and adding the prefix will make everything unique.


Other than that, great job with the tutorial Smile
Quote:Also function names in a plugin file need to be prefixed with the filename excluding the .php extension
I have agreed with this already... but just to clear for those new, the function still works without a prefix, but prefix is preferable just not to have collision with another one of the same name.

I will add this to my tutorial,
Great job. Thanks Zaher!
Pages: 1 2 3