vb.org Archive

vb.org Archive (https://vborg.vbsupport.ru/index.php)
-   vBulletin 3 Articles (https://vborg.vbsupport.ru/forumdisplay.php?f=187)
-   -   [How to] Write Plug-ins/Hooks (https://vborg.vbsupport.ru/showthread.php?t=82625)

Brad 06-06-2005 10:00 PM

[How to] Write Plug-ins/Hooks
 
This is your basic guide to making plug-ins and hooks, as we are all coming over from the 'hack the files' mentality I am posting this thread more for people that are used to hacking the 3.0.x source code and looking to port modifications. Although a newbie should be able to come away with a good understanding of how to do this to :).

[high]Things to consider[/high]

Ok before we get started here are a few things to keep in mind. First and foremost plug-ins are stored in the database, serlized, unserlized and evaled on page generation. If you code preforms badly with such a wrapper forget making a plug-in and just hack it, trust me you'll thank yourself for it when your forum gets large.

Second thing, hooks will not get you into every corner of the code. Some things will always be done best with a hack. For example if you are looking to add something but need to query the db for some extra data and want to avoid and extra query (in other words you are going to modify an existing one with a join or such) forget it and hack it in, there are no hooks that let you modify existing queries.

Last but not least hooks are not magic, don't add a million of them and expect your forum to run as fast as it did when you first installed it. Only plug-in/hack-in what you really need!

Ok enough with the boring stuff, lets add our first plug-in!

[high]Basics:[/high]

First thing you need to do is make sure plug-ins are enabled, you can find this option in the admin cp by browsing to vBoptions -> Select Plugin/Hook System from menu -> Set Enable Plugin/Hook System to yes.

Now head to the add plug-in page located at http://www.yoururl.com/forum/admincp/plugin.php?do=add

Lets go over what all these options mean:

Hook Location: This is where the php code will be executed at, hook locations at defined all across vBulletin. You can find them by opening and php file and searching for the var $hook. When you find a bit of php code like this you have found a hook:

PHP Code:

($hook vBulletinHook::fetch_hook('showthread_postbit_create')) ? eval($hook) : false

See that bit fetch_hook('showthread_postbit_create')? Well the text "showthread_postbit_create" is the name of this hook, if you look in the drop down you will find that listed in there somewhere. Incase you are wondering that hook is located in showthread.php at line 1012

Title: This is the title of your plug-in, use a good name because this is the only thing you have to identify the plug-in in the plug-in manager.

Plugin PHP Code: Can you guess? This is where you put your custom php code, on page generation it is executed at the hook location in the .php files. Note that you do not need <?php ?> tags here, in other words:

wrong:

PHP Code:

<?php $var true?>

Right:

PHP Code:

$var true

Plugin is Active: Allows you to turn the plug-in on/off without removing it. If set to 'yes' php code is ran on page execution.

Edit - Thanks to Revan for this addition:

Quote:

[high]vbulletin_plugins.xml[/high]
If you need to tell your users to add a lot of plugins, this will become tedious as much copy/pasting is required. A simpler way would be to use the .xml import. It works just like the importing of templates and phrases, by adding the contained code as a Plugin.
The correct format for a plugin.xml file is like so (thanks to Live Wire):
Code:

<?xml version="1.0" encoding="ISO-8859-1"?>
<plugins>
        <plugin active="1">
                <title>vB Category Icons</title>
                <hookname>forumdata_start</hookname>
                <phpcode><![CDATA[$this->validfields['forumhomeicon'] = array(TYPE_STR, REQ_NO);]]></phpcode>
        </plugin>
</plugins>

Explanations:
<plugin active="1"> - The 'active' attribute determines the default value of 'Plugin is Active' in the Plugin Manager.
<title></title> - Self explanatory, it is the 'Title' field in the Manager.
<hookname></hookname> - The 'Hook Location' you would select.
<phpcode><![CDATA[ ]]></phpcode> - Anything added in the space between these is added to the 'Plugin PHP Code' part.
[high]Tips[/high]

At first approach coding the plug-in like a hack.

Open the php file you would normally edit and get your mind around the new code.

Once you have a good understanding of the new code you should start looking for where you need to add your custom php. Once you find the right location in the code start looking for a nearby hook, if you don't see one you can work with you are out of luck! :(

Edit- Thanks to KirbyDE for his addition:

Quote:

Something that might come in handy for those developing Plugins:

In class_hook.php FIND
PHP Code:

function &fetch_hook($hookname)


BELOW that ADD
PHP Code:

DEVDEBUG("Calling Hook $hookname"); 

Then (if debug mode is turned on) you can see which hooks are being called, and when they are being called.
Once you find your hook note its name and apply your changes in the admincp, from here on out you are on your own :)

amykhar 06-09-2005 05:01 PM

Thanks, Brad. It's nice to have a plugins for dummies tutorial to get started with :)

Logikos 06-09-2005 05:49 PM

Excellent! Now i understand the hooks system.

amykhar 06-09-2005 05:55 PM

I'm glad somebody does :D

The first hack I looked at porting doesn't look like it will be able to use the hook system, unfortunately. I really wish jelsoft had come up with a more extensible way to format queries. If the queries have to be changed, we run into the same problems we had before.

I would love it if the arguments to a query were set up in an array format that could be easily added to. That way, if we are forced to add into an existing query, the user could modify the arguments to the query rather than the query itself. It would be so much easier to prevent hack conflicts and installation errors that way.

Amy

Brad 06-09-2005 05:58 PM

Amy: If you see a good spot for a hook request it at vBulletin.com, this is one of the main reasons we are encourged to hack the beta :)

Logikos 06-09-2005 06:02 PM

Brad i have a question, I ported my icons hack. But i have to hack the admincp/forum.php for the option to show up in the admincp. Does the Hooks system work in the admincp/ dir? From what i read in the code it doesn't.

Maybe you have another solution to this.

Its just this:
PHP Code:

print_textarea_row('description''forum[description]'$forum['description']); 


Andreas 06-09-2005 06:03 PM

Unfortunately there are currently no hooks within the AdminCP.
Therefor you will have to modify the files.

Logikos 06-09-2005 06:07 PM

Quote:

Originally Posted by KirbyDE
Unfortunately there are currently no hooks within the AdminCP.
Therefor you will have to modify the files.

Then whats the point! Bah, so much for that... :p

Brad 06-09-2005 06:09 PM

Quote:

Originally Posted by Live Wire
Then whats the point! Bah, so much for that... :p

Less file modification = easier to upgrade.

Like I said, you see a useful place for a hook request one at vBulletin.com! :)

amykhar 06-09-2005 06:09 PM

I am not sure a hook would help my problem. For example, if you look at index.php, you see a query to get the logged in users.

Code:

$forumusers = $db->query_read("
                SELECT
                        user.username, (user.options & " . $vbulletin->bf_misc_useroptions['invisible'] . ") AS invisible, user.usergroupid,
                        session.userid, session.inforum, session.lastactivity,
                        IF(displaygroupid=0, user.usergroupid, displaygroupid) AS displaygroupid
                FROM " . TABLE_PREFIX . "session AS session
                LEFT JOIN " . TABLE_PREFIX . "user AS user ON(user.userid = session.userid)
                WHERE session.lastactivity > $datecut
                " . iif($vbulletin->options['displayloggedin'] == 1, "ORDER BY username ASC") . "
        ");

Now, I need to know the useragent field from the sessions table. So, I have to change the query to:

Code:

$forumusers = $db->query_read("
                SELECT
                        user.username, (user.options & " . $vbulletin->bf_misc_useroptions['invisible'] . ") AS invisible, user.usergroupid,
                        session.userid, session.inforum, session.lastactivity,session.useragent,
                        IF(displaygroupid=0, user.usergroupid, displaygroupid) AS displaygroupid
                FROM " . TABLE_PREFIX . "session AS session
                LEFT JOIN " . TABLE_PREFIX . "user AS user ON(user.userid = session.userid)
                WHERE session.lastactivity > $datecut
                " . iif($vbulletin->options['displayloggedin'] == 1, "ORDER BY username ASC") . "
        ");

I don't think a hook would change that for me. But, if each query had it's arguments defined in a structure that defined the columns to read, update, or insert and had a way to set up joins, etc. outside the query in a clean, easy to modify way (such as adding arguments to an array), then we could hack right in there without worry of conflicts or sql syntax issues.

Amy

Andreas 06-09-2005 06:15 PM

Hmm .... a Query class!
That's what would be needed.

Example
PHP Code:

$query->add_join($table$alias$condition);
$query->add_fields(array('field1''field2' => 'fieldalias'));
$query->add_condition($operator$condition);
... 


amykhar 06-09-2005 06:26 PM

Quote:

Originally Posted by KirbyDE
Hmm .... a Query class!
That's what would be needed.

Example
PHP Code:

$query->add_join($table$alias$condition);
$query->add_fields(array('field1''field2' => 'fieldalias'));
$query->add_condition($operator$condition);
... 


Exactly!

Fusion 06-09-2005 07:26 PM

Quote:

Originally Posted by KirbyDE
Hmm .... a Query class!
That's what would be needed.

Example
PHP Code:

$query->add_join($table$alias$condition);
$query->add_fields(array('field1''field2' => 'fieldalias'));
$query->add_condition($operator$condition);
... 


Actually, that's an excellent idea.

Revan 06-09-2005 09:12 PM

I have an addition to this tutorial:

[high]vbulletin_plugins.xml[/high]
If you need to tell your users to add a lot of plugins, this will become tedious as much copy/pasting is required. A simpler way would be to use the .xml import. It works just like the importing of templates and phrases, by adding the contained code as a Plugin.
The correct format for a plugin.xml file is like so (thanks to Live Wire):
Code:

<?xml version="1.0" encoding="ISO-8859-1"?>
<plugins>
        <plugin active="1">
                <title>vB Category Icons</title>
                <hookname>forumdata_start</hookname>
                <phpcode><![CDATA[$this->validfields['forumhomeicon'] = array(TYPE_STR, REQ_NO);]]></phpcode>
        </plugin>
</plugins>

Explanations:
<plugin active="1"> - The 'active' attribute determines the default value of 'Plugin is Active' in the Plugin Manager.
<title></title> - Self explanatory, it is the 'Title' field in the Manager.
<hookname></hookname> - The 'Hook Location' you would select.
<phpcode><![CDATA[ ]]></phpcode> - Anything added in the space between these is added to the 'Plugin PHP Code' part.


// end my contribution (if this is being merged into the first post, nothing below this is to be included)

And just for all you lazy people out there, here is an empty set, ready for copypasting into a blank xml file (only the <?xml and <plugins> tags need to be there already):
Code:

        <plugin active="1">
                <title></title>
                <hookname></hookname>
                <phpcode><![CDATA[]]></phpcode>
        </plugin>

I hope this helps hack writers that want to minimise the hassle for the end-user :)

Cap'n Steve 06-10-2005 06:28 AM

Quote:

Originally Posted by Brad.loo
First and foremost plug-ins are stored in the database, serlized, unserlized and evaled on page generation. If you code preforms badly with such a wrapper forget making a plug-in and just hack it, trust me you'll thank yourself for it when your forum gets large.

Could you elaborate on this? Are there some known problems with eval() or something else?

Logikos 06-10-2005 06:45 AM

Quote:

Originally Posted by Cap'n Steve
Could you elaborate on this? Are there some known problems with eval() or something else?

What he means is, if your code is just;
PHP Code:

echo $threadid

You better off hacking it, as if you add it to the plugin area, it has to call a query just to call that line.

The above is just a stupid example for you to understand better. Obviously we won't be using a echo in our hacks.

Paul M 06-10-2005 06:54 AM

Quote:

Originally Posted by Revan
I have an addition to this tutorial:

<snip>

If you have developed the plugin on your own board then all you need do is use the plugin manager's download feature to create your ready made xml file for others to use.

Marco van Herwaarden 06-10-2005 08:43 AM

Guys (and girls) please don't use threads in the How-To's forum to say thank you or discuss things. Only post How-To's and additions to them so we can get clean instructions on how to solve problems.

Andreas 06-10-2005 02:15 PM

Something that might come in handy for those developing Plugins:

In class_hook.php FIND
PHP Code:

function &fetch_hook($hookname)


BELOW that ADD
PHP Code:

DEVDEBUG("Calling Hook $hookname"); 

Then (if debug mode is turned on) you can see which hooks are being called, and when they are being called.

dwh 06-15-2005 06:43 AM

I'm trying to get a handle on these hooks. What if you want to write custom code and just hook it into opening a new pm, or starting a new thread. How would that work?

Also is there a difference between a Plug-In and a Hook or are they the same thing?

Marco van Herwaarden 06-15-2005 07:50 AM

A Plug-In is the code that uses a Hook to attach itself to exisitng code.

Revan 06-15-2005 08:34 AM

Quote:

Originally Posted by Live Wire
hacking it, as if you add it to the plugin area, it has to call a query just to call that line.

Not true. I tried adding 5 plugins to the showthread all with useless variable definitions, and it did not call any additional queries.

dreck 06-18-2005 05:40 AM

Thanks Brad

deathemperor 06-25-2005 02:41 PM

When adding new plugin, there's a input field name 'Developer Key' . Do you know what it does, Brad ? the strange thing is that I installed 2 vb3.5 board, one has it but the another doesn't

Edit: it shows when you enabled debug mode

dwh 06-29-2005 10:34 PM

Quote:

Originally Posted by MarcoH64
A Plug-In is the code that uses a Hook to attach itself to exisitng code.

I think I get it. So it's like "including" some code in a certain place in the code? Is that all??

I thought it was going to be more of a standardized method of calling certain functions.

Like if you want to start a new thread each time someone registers, instead of copying all the code around creating a new thread you just call build_new_post() (which surprisingly wasn't enough in vb 3.07). Or for a custom PM every time someone reaches 100 posts you call build_new_pm().

It sounds like this is very different than I was expecting.

Marco van Herwaarden 06-30-2005 07:37 AM

You can see a Plugin as an externally definable include.

What you are talking about has nothing to fo with Plugins, but with standard functions offered, and maybe with approaching this as OOP classes.

dwh 06-30-2005 03:13 PM

Quote:

Originally Posted by MarcoH64
You can see a Plugin as an externally definable include.

What you are talking about has nothing to fo with Plugins, but with standard functions offered, and maybe with approaching this as OOP classes.

Thanks for explaining it.

To tell the truth, I'm pretty disappointed by this. It sounds like adding a ton of complexity to something as simple as inserting a bit of code in a file. This might actually make upgrades worse. Includes need to happen at the right place. I'd rather look at the code on one page when upgraded to see if the placement has to change. Now if there are 3 mods working on a similar piece of code, it will be much harder to get a feel of the flow of the code. As it is, with regular code, to follow the logic you need to look at all the included files. I've found that to be quite a pain several times, but there it's understandable. I dunno. Maybe it will all work out, but I'm wondering if this is such a good thing...

What's the big benefit? Just to make mods easier to install for newbies?

(Just in case this comes off wrong, I'm not trying to say this in a negative manner or just to nag...I also want to check if I'm understanding this properly and give feedback about it. As always, I want it clear that I love vb!)

sketch42 08-09-2005 03:40 AM

ok im a bit confused at how these hooks work.. i mean i understand the basics of it but i think i need a bit more explanation

now say i wanted to add my code into /admincp/user.php
and i want to modify this area

PHP Code:

// ###################### Start Edit Access #######################
if ($_REQUEST['do'] == 'editaccess')
{
        if (!
can_administer('canadminpermissions'))
        {
                
print_cp_no_permission();
        }

        
$vbulletin->input->clean_array_gpc('r', array(
                
'userid' => TYPE_INT
        
));

        
$user $db->query_first("SELECT username, options FROM " TABLE_PREFIX "user WHERE userid = " $vbulletin->GPC['userid']);

        
$accesslist $db->query_read("SELECT * FROM " TABLE_PREFIX "access WHERE userid = " $vbulletin->GPC['userid']);

        
//echo '<h1>$db->numrows($accesslist) = ' . $db->num_rows($accesslist) . '<br />user.hasaccessmask = ' . ($user['options'] & $vbulletin->bf_misc_useroptions['hasaccessmask'] ? 'yes' : 'no') . '</h1>';

        
while ($access $db->fetch_array($accesslist))
        {
                
$accessarray[$access['forumid']] = $access;
        }

        
print_form_header('user''updateaccess');
        
construct_hidden_code('userid'$vbulletin->GPC['userid']);

        
print_table_header($vbphrase['edit_access_masks'] . ": <span class=\"normal\">$user[username]</span>"20);
        
print_description_row($vbphrase['here_you_may_edit_forum_access_on_a_user_by_user_basis']);
        
print_cells_row(array($vbphrase['forum'], $vbphrase['allow_access_to_forum']), 0'thead', -2);
        
print_label_row('&nbsp;''
                <input type="button" value="' 
$vbphrase['all_yes'] . '" onclick="js_check_all_option(this.form, 1);" class="button" />
                <input type="button" value=" ' 
$vbphrase['all_no'] . ' " onclick="js_check_all_option(this.form, 0);" class="button" />
                <input type="button" value="' 
$vbphrase['all_default'] .'" onclick="js_check_all_option(this.form, -1);" class="button" />
        '
); 

so the closest hook i could find is this
PHP Code:

        ($hook vBulletinHook::fetch_hook('useradmin_update_save')) ? eval($hook) : false

which is 20 lines above
so would i need to do this?
PHP Code:

       ($hook vBulletinHook::fetch_hook('useradmin_update_save')) ? eval($hook) : false;

        
// save data
        
$userid $userdata->save();
        if (
$vbulletin->GPC['userid'])
        {
                
$userid $vbulletin->GPC['userid'];
        }

        
// #############################################################################
        // now do the redirect

        
if ($vbulletin->GPC['modifyavatar'])
        {
                
define('CP_REDIRECT'"usertools.php?do=avatar&amp;u=$userid");
        }
        else if (
$vbulletin->GPC['modifyprofilepic'])
        {
                
define('CP_REDIRECT'"usertools.php?do=profilepic&amp;u=$userid");
        }
        else
        {
                
define('CP_REDIRECT'"user.php?do=modify&amp;u=$userid. ($userdata->insertedadmin '&insertedadmin=1' ''));
        }

        
print_stop_message('saved_user_x_successfully'$user['username']);
}

// ###################### Start Edit Access #######################

BLAH BLAH BLAH CUSTOM PHP CODE 

or am i way off base?

dwh 08-15-2005 10:22 AM

Quote:

Originally Posted by Fusion
Actually, that's an excellent idea.

Has there been any traction on this idea? Does vb think of doing this in a future version?

DJ RRebel 09-09-2005 09:07 PM

yeah ... I'd have to agree that most of the hacking I've done and would like to do involves calling extra fields in the database ... I haven't really looked into it yet, but it would seem that it would make alot of sense to have hooks at the end of some select statements.

... or better yet, have "replacement hooks" for some queries ... where the default querry would be used ... but if there was an active hook for that location, that segment would be replaced. Although I'm guessing this 2nd option would make you have to change your hook after upgrading if the original query required changes.

Anyhow ... I guess I should try a Plug-in or two before I make too many more suggestions! lol

PennylessZ28 10-01-2005 09:08 PM

Nice, this is going to help alot as I'm starting to dabble in writting some custom stuff for a concept board I am creating on my test server.

Wired1 10-20-2005 01:44 AM

Don't know if this was mentioned anywhere yet, but you can create your own hook locations. I did it for one of my hacks.

pyro.699 10-28-2005 07:11 PM

is there a list? any where, that tells you what each hook location is for?

(i need on that runs when your posting a new thread)

timetunnel 11-12-2005 03:12 AM

Hello.

This thread has help me figure out the 'hook' system (ref: https://vborg.vbsupport.ru/showthread.php?t=100597) finally (actually, I hadn't needed it previously except for variable creation, until yesterday). You'll see my interpretation in that ref'd thread.

Hope it helps. :squareeyed:

P.S. This hook system is brilliant! I've worked with many other software programs and the vB structure is at the top on the pyramid in its design.

monotreme 11-23-2005 05:55 PM

Quote:

Originally Posted by Wired1
Don't know if this was mentioned anywhere yet, but you can create your own hook locations. I did it for one of my hacks.

To create your own hook locations (I'm talking to Wired1) don't you have to hack the core code? Still I guess its better to insert one line hook definition than 60 lines of code.

davidw 11-23-2005 06:16 PM

<font color="SeaGreen">* christianb subscribes to this thread</font>

Wired1 11-23-2005 07:41 PM

Quote:

Originally Posted by monotreme
To create your own hook locations (I'm talking to Wired1) don't you have to hack the core code? Still I guess its better to insert one line hook definition than 60 lines of code.

Exactly, 1 line is better than 60. It's also much harder for someone to screw up and/or fix 1 line than 60 as well (e.g. misplacement, typo, etc).

If anyone's interested I could write up a quick tutorial about it.

Cap'n Steve 11-24-2005 04:27 AM

Quote:

Originally Posted by monotreme
To create your own hook locations (I'm talking to Wired1) don't you have to hack the core code? Still I guess its better to insert one line hook definition than 60 lines of code.

You can also add hooks to your own code, which is what I thought he was talking about.

timetunnel 11-24-2005 06:37 AM

Hello.

Wired1, any tutorials are always welcomed.

Cap'n Steve, I'd also like to know how to add hooks to custom code.

Thanks folks.

Wired1 11-24-2005 05:24 PM

Yeah, you can add hooks anywhere. It's pretty easy actually.

Can't remember which hack I added a custom hook for (it's either the extra info one or the profile one), but one of them has the basic instructions for it. I'll try to whip one up in a couple of days.


All times are GMT. The time now is 08:58 PM.

Powered by vBulletin® Version 3.8.12 by vBS
Copyright ©2000 - 2025, vBulletin Solutions Inc.

X vBulletin 3.8.12 by vBS Debug Information
  • Page Generation 0.01793 seconds
  • Memory Usage 1,913KB
  • Queries Executed 10 (?)
More Information
Template Usage:
  • (1)ad_footer_end
  • (1)ad_footer_start
  • (1)ad_header_end
  • (1)ad_header_logo
  • (1)ad_navbar_below
  • (5)bbcode_code_printable
  • (15)bbcode_php_printable
  • (16)bbcode_quote_printable
  • (1)footer
  • (1)gobutton
  • (1)header
  • (1)headinclude
  • (6)option
  • (1)pagenav
  • (1)pagenav_curpage
  • (1)pagenav_pagelink
  • (1)post_thanks_navbar_search
  • (1)printthread
  • (40)printthreadbit
  • (1)spacer_close
  • (1)spacer_open 

Phrase Groups Available:
  • global
  • postbit
  • showthread
Included Files:
  • ./printthread.php
  • ./global.php
  • ./includes/init.php
  • ./includes/class_core.php
  • ./includes/config.php
  • ./includes/functions.php
  • ./includes/class_hook.php
  • ./includes/modsystem_functions.php
  • ./includes/class_bbcode_alt.php
  • ./includes/class_bbcode.php
  • ./includes/functions_bigthree.php 

Hooks Called:
  • init_startup
  • init_startup_session_setup_start
  • init_startup_session_setup_complete
  • cache_permissions
  • fetch_threadinfo_query
  • fetch_threadinfo
  • fetch_foruminfo
  • style_fetch
  • cache_templates
  • global_start
  • parse_templates
  • global_setup_complete
  • printthread_start
  • pagenav_page
  • pagenav_complete
  • bbcode_fetch_tags
  • bbcode_create
  • bbcode_parse_start
  • bbcode_parse_complete_precache
  • bbcode_parse_complete
  • printthread_post
  • printthread_complete