Go Back   NuHIT - SEO, Wiki, Forums > Site, Feedback and Miscellaneous > Coders
Reply
 
Thread Tools
 
#1
Old 01-12-2007, 02:47 PM
 
ElfMage
nuHIT Team
 
ElfMage's Avatar
 
Join Date: Aug 2006
Posts: 3,603
ElfMage is offline  

Custom Navits Mod suggestion


Pegasus,

I was looking at your Custom Navbits mod and here are a couple of suggestions:

Rather than creating a new table, with the extra query added; you could use an entry in vBulletin's 'setting' table.

This one setting would be an array with all of your custom navbits entries serialized into a string.

So, to save a new record you would do:
PHP Code:
$all_custom_navbits[] = $new_navbit_row;
$vbulletin->options['ces_custom_navbits'] = serialize$all_custom_navits );
build_options(); 
And then to load them you could do the following:
PHP Code:
$all_custom_navbits unserialize($vbulletin->options['ces_custom_navbits'] );

// then find the navbits for the current script...
$cnav = array();
foreach (
$all_custom_navbits as $navbit_iter)
   if (
$navbit_iter['bitactive'])
      
$cnav"{$navbit_iter['bitorder']}" ] = $navbit_iter;

// order by bitorder
ksort$cnav ); 

By doing this you don't have to create the table navbits, and all vbulletin options are cached in memory by vBulletin, so there is no extra query.

Also, by going this route, the installation instructions could go from modifying every php where a user wants to have custom navbits to modifying one file: functions.php, add the following line to construct_navbits:

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

So, advantages of this suggestion:
1) No new tables
2) No new queries
3) Only add one line of code.

Let me know what you think.
Reply With Quote

#2
Old 01-13-2007, 10:15 PM
 
Excellent suggestions, ElfMage. Your last one (the single file edit) seems most practical for including in the next release 1.2.0, since we are already in the testing phase. When this is finally released, I would ask you to review the new database structure.

The code was completely rewritten (twice) for the next version for improved speed on the customized pages themselves, the number of columns in the navbits table has nearly tripled, and each page creates many more than a single row. I am not sure if this would be too much to store in vBulletin's memory - at least, I don't have the experience working with arrays of that size.

The biggest was my the Presets array, which I was very impressed with myself for getting to work, since in the past arrays have just given me headaches - that is foreach ($array AS $key => $value) just didn't work for me until recently (maybe it was my old server?):

PHP Code:
$presets = Array(
     
'FORUM_HOME' => Array(
           
'name' => 'FORUM_HOME',
           
'url'     => $forumhome[0],
           
'text'   => $forumhome[1]
     ),
    
'THIS_PAGE' => Array(
           
'name' => 'THIS_PAGE',
           
'url'     => '#',
           
'text'   => $this_page
     
),
    
'WIKI_NAME' => Array(
           
'name' => 'WIKI_NAME',
           
'url'     => $wiki_info[0],
           
'text'   => $wiki_info[1]
     )
); 
where $forum_home and $wiki_info were arrays created from vBulletin settings in other function calls. By the way, if anyone has any ideas for additional presets, let me know.

ElfMage, after reviewing the new database structure, if you still believe caching all that information in vBulletin settings is more efficient than making a single query, don't hesitate to suggest this again.

Again thanks for the heads up on just editing functions.php, I could not find what file construct_navbits() was in when I wrote version 1.0.0.

EDIT: It just occurred to me... How would I pass variables into the construct_navbits() function without modifying the individual pages? Not all variables function as globals. This could destroy the conditionals feature - where say you only wanted the new navbits to appear on forumdisplay when the forum id was 2. I don't know if there is a workaround for this.

Last edited by pegasus : 01-13-2007 at 10:29 PM.
pegasus
Premium Member
 
Join Date: Nov 2006
Location: New York
Posts: 364
Send a message via AIM to pegasus
pegasus is offline  
Reply With Quote

#3
Old 01-14-2007, 10:49 AM
 
ElfMage
nuHIT Team
 
ElfMage's Avatar
 
Join Date: Aug 2006
Posts: 3,603
ElfMage is offline  
Thanks pegasus.

Regarding the size of the array, I don't think this will be an issue. As long as you don't have thousands of records, you are Ok. I think that using some memory is better than an extra DB query, even more so for high traffic forums.

Regarding the variables (forumid, etc) just use the following:
PHP Code:
global $forumid$foruminfo
And add all the global variables you want to get access to.
Reply With Quote

#4
Old 01-14-2007, 12:21 PM
 
Okay, I would have to add another Admin field to fill in the global variables you will be using in conditions, since some add-on scripts use their own... This would add another eval() to each record in the check cycle...

Also I'm not exactly sure how to run queries on an array of this sort that is not stored in MySQL. Is there a function set I should read up on? Also not exactly positive on the most efficient way to structure additional records - or is the $presets array a good example of that?

EDIT: This post just made me realize I could cut 3 or 4 eval() calls out of runtime.

Last edited by pegasus : 01-14-2007 at 12:27 PM.
pegasus
Premium Member
 
Join Date: Nov 2006
Location: New York
Posts: 364
Send a message via AIM to pegasus
pegasus is offline  
Reply With Quote

#5
Old 01-14-2007, 12:34 PM
 
ElfMage
nuHIT Team
 
ElfMage's Avatar
 
Join Date: Aug 2006
Posts: 3,603
ElfMage is offline  
Well it depends.

Regarding queries in arrays, that's one of the disadvantages.

You either have to iterate through the whole array, or you can use array keys and then use array_search to find something.

That's why this method only works when you have a reasonable number of records.

Actually, this is what vBulletin uses for its internal 'Datastores'. Basically they load everything in memory, and save it as an array in a in-memory SQL table. (different type of sql table)

By storing your array in vBulletin's options, you benefit from what they are doing.

How to best organize this, would depend on your data.

For instance lets assume that you currently have 2 sql tables, navbits and presets.

You could follow one of these paths:
Have two vbulletin settings: ces_navbits and ces_presets.

so when your code starts, you 'load' the settings into your variables like this:

$ces_navbits = unserialize( $vbulletin->options['ces_navbits'] );
$ces_presets = unserialize( $vbulletin->options['ces_presets'] );

Or, you could store all your data in ONE vbulletin setting.

$ces_data = unserialize( $vbulletin->options['ces_data'] );
$ces_navbits = $ces_data['ces_navbits'];
$ces_presets = $ces_data['ces_presets'];

In vbWiki Pro, I used this method to save the Forum Headers into the vb's forum table. Take a look at vbWikiPro_Cron.php line 176. There we save the array of links.

Then we read it back in vbWikiPro_Plugins.php, line 81.
Reply With Quote

#6
Old 01-14-2007, 12:55 PM
 
Once I get v1.2.0 out, I will start testing this for the next version to see if it ends up being more or less server intensive to iterate through and evaluate that many rows.

Thanks for the explanation. Is there a way to control which options are loaded into memory with the plug-in system? Say, if the navbits plugin is disabled, then it doesn't even bother loading the $ces_data.
pegasus
Premium Member
 
Join Date: Nov 2006
Location: New York
Posts: 364
Send a message via AIM to pegasus
pegasus is offline  
Reply With Quote

#7
Old 01-14-2007, 12:59 PM
 
ElfMage
nuHIT Team
 
ElfMage's Avatar
 
Join Date: Aug 2006
Posts: 3,603
ElfMage is offline  
Cool. Thanks.
Reply With Quote

#8
Old 01-14-2007, 01:18 PM
 
Above you mentioned how to add and read records. How would I replace records using this method, say when the user updates their navbits, or perhaps deletes them entirely?

Would I have to use array_keys() and array_search() until I found the one to overwrite, or does vBulletin offer another way?
pegasus
Premium Member
 
Join Date: Nov 2006
Location: New York
Posts: 364
Send a message via AIM to pegasus
pegasus is offline  
Reply With Quote

#9
Old 01-14-2007, 01:28 PM
 
ElfMage
nuHIT Team
 
ElfMage's Avatar
 
Join Date: Aug 2006
Posts: 3,603
ElfMage is offline  
You could do this:
foreach ($arr as $k => $v)
{
if ($v is what I want to delete)
{
unset( $arr[ $k ] );
break;
}
}

However this is inefficient. A better way is like this.
To add a record to an array, use the record id as the array key:
$arr[ $record['id'] ] = $record;

Then when you want to update it, you simply repeat the code above and it replaces the previous record.

When you want to delete it, simply do:
unset( $record['id'] );

In these examples, it is assumed that the record has a field named 'id' that contains the record id, that should be replaced with whatever field name is appropriate.
Reply With Quote

#10
Old 01-14-2007, 01:58 PM
 
That'll work.
pegasus
Premium Member
 
Join Date: Nov 2006
Location: New York
Posts: 364
Send a message via AIM to pegasus
pegasus is offline  
Reply With Quote

#11
Old 01-15-2007, 12:59 AM
 
pegasus
Premium Member
 
Join Date: Nov 2006
Location: New York
Posts: 364
Send a message via AIM to pegasus
pegasus is offline  
Okay, this worked for the conditionals, but now I have no clue how to make this work.

PHP Code:
global eval("return $mabbits['bit_globals'];"); 
EDIT: Don't worry about it. It turns out placing the hook inside the construct_navbits function breaks a large part of its functionality even while using the appropriate globals. The problem lies in only being able to return one thing from a function, and without a file edit to pull the pieces out of a returned array... so sadly, the file edits cannot be reduced.

EDIT: Still regarding eval(), I'm pretty much ready to release v1.2.0... however, I don't know of a way to both eval() $vbphrases and $vbulletin etcs AND escape single/double quotes. Escaping the quotes parses the variables makes eval() see [ and ] as bad characters. If I don't escape the quotes then we get database errors, and I feel this is a limitation, now users have to employ " and whatever the other one is that I never use.

Is there a way I can accomplish both without compromise? The current code, basically:
PHP Code:
function process_piece$broken$presets$bits$ct )
{

    global 
$vbulletin$vbphrase$db;
    
// in case someone prefers over $vbulletin->options
    
$vboptions =& $vbulletin->options;

    foreach (
$broken AS $both => $glass)
    {
        foreach (
$presets AS $settings)
        {
            if (
$glass == $settings['name'])
            {
                
$broken["$both"] = $settings["$both"];
            }
            else if (
$glass == $settings['name'])
            {
                
$broken["$both"] = $settings["$both"];
            }
        }
    }

    
$bits['bit_url_parsed'] = eval('return "' $broken['url'] . '";');
    
$bits['bit_text_parsed'] = eval('return "' $broken['text'] . '";'); 
Currently the above code is parsing $vbulletin vars and settings but not quotation marks.

Last edited by pegasus : 01-15-2007 at 08:03 AM.
Reply With Quote

#12
Old 01-15-2007, 10:47 AM
 
Quote:
Originally Posted by pegasus View Post
Okay, this worked for the conditionals, but now I have no clue how to make this work.

PHP Code:
global eval("return $mabbits['bit_globals'];"); 
EDIT: Don't worry about it. It turns out placing the hook inside the construct_navbits function breaks a large part of its functionality even while using the appropriate globals. The problem lies in only being able to return one thing from a function, and without a file edit to pull the pieces out of a returned array... so sadly, the file edits cannot be reduced.
I don't understand what you mean.

In the hook all you need to do is modify the variable $nav_array

For instance, insert your hook here:

Code:
function construct_navbits($nav_array)
{
	global $pagetitle, $stylevar, $vbulletin, $vbphrase, $show;

($hook = vBulletinHook::fetch_hook('ces_navbits')) ? eval($hook) : false;   // this is the new hook
Then in the plugin code for the hook ces_navbits, try this:

Code:
$nav_array['http://www.vbulletin.org'] = 'vBulletin.oRG';
And that's it. I just tested it and it works.

So the idea is that you don't touch $navbits anymore since that has no meaning at this point.

In your hook (and in the construct_navbits function), to mess with the navbits, simply refer to $nav_array.

Quote:
EDIT: Still regarding eval(), I'm pretty much ready to release v1.2.0... however, I don't know of a way to both eval() $vbphrases and $vbulletin etcs AND escape single/double quotes. Escaping the quotes parses the variables makes eval() see [ and ] as bad characters. If I don't escape the quotes then we get database errors, and I feel this is a limitation, now users have to employ " and whatever the other one is that I never use.
I am not sure I follow the intent of the code, but regarding the eval woes, try this:

Code:
	eval( '$bits["bit_url_parsed"] = "' . $broken['url'] . '";' );
    eval( '$bits["bit_text_parsed"] = "' . $broken['text'] . '";' );
Not sure if that's what you wanted...
ElfMage
nuHIT Team
 
ElfMage's Avatar
 
Join Date: Aug 2006
Posts: 3,603
ElfMage is offline  
Reply With Quote

#13
Old 01-15-2007, 02:58 PM
 
pegasus
Premium Member
 
Join Date: Nov 2006
Location: New York
Posts: 364
Send a message via AIM to pegasus
pegasus is offline  
Quote:
Originally Posted by ElfMage
I don't understand what you mean.

In the hook all you need to do is modify the variable $nav_array
This is what I was doing. I was concerned about other values the script relied on other than the $navbits. But never mind, I figured out how to get my values out.

Quote:
Originally Posted by ElfMage
PHP Code:
eval( '$bits["bit_url_parsed"] = "' $broken['url'] . '";' );
    eval( 
'$bits["bit_text_parsed"] = "' $broken['text'] . '";' ); 
This didn't work either. I had to use htmlspecialchars().

EDIT: I can't destroy $nav_array with unset() or anything else when inside construct_navbits(). The new navbits are just added to the old ones. I would think that if I destroy it within t