View Full Version : Making my own plugin, starting from scratch, some PHP questions (my ongoing adventure
jwocky
05-30-2006, 10:13 PM
** Hopefully this thread can become a resource for other newbies who want to learn how to do some PHP coding in relation to a vB hack **
Hey everyone, I've been a user here for a while now downloading the great plugins we have in this community. Now I need something custome but couldnt find anyone to take on the project, so I figured, what the hell.. I'll try to make one myself.
Now to begin with, I have no knowledge of PHP whatsoever, but i'm not a total idiot, and a pretty quick learner.. I also have general experience with coding from the old basic, then C++ days so I know the general concepts, but not the specific calls anymore.
I've been furiously reading the tutorials from the sites that have been generously stickied in this subforum.. but as always, the best way to learn is to learn by doing, so i've started on my project.
Now with that background out of the way, I've started on my voyage to making this custom script.
It has many stages, but I will try to build it step by step.
What my script generally will do in the end will be to take data about a certain user (data would be the users name, user start date and a user end date) and then do some functions based on whether or not todays date matches the pre-definined user start date and end-date.
As I dont want to mess with my databse and its tables, I decided to make a simple text file with the information as such:
UserName, Start Date, End Date
UserName, Start Date, End Date
UserName, Start Date, End Date
First thing for me was to poll todays date, which I have been able to do, yay!
Second is to poll data from the text file.
I've been able to pull the data using this call:
function getnames()
{
$data = file_get_contents ( "users.txt" );
return $data;
}
However now i'm stuck on how to parse the data properly.. Can anyone give me pointers on how to parse this info ? Ideally i'd like to separate the username, start date and end date into separate variables so i can compare them to todays date...
Thanks for any thoughts or answers!
Adrian Schneider
05-30-2006, 11:35 PM
function getNames()
{
$lines = file('your_file.txt');
$users = array();
foreach ($lines as $line)
{
$users[] = explode(', ', trim($line));
}
return $users;
}Similar to yours...
file() (http://www.php.net/file) takes the first parameter, and returns an array containing all of the lines.
Then, I created an array which I will load with all the user data...
foreach (http://www.php.net/foreach) is a loop. For each (array) $lines, set $line equal to that row. I then add a new row the the $users array, containing an array for that current user.
Using explode() (http://www.php.net/explode), you can take a string, and turn it into an array, using the first parameter as a delimiter, and the second as the source array. I used the function trim() (http://www.php.net/trim) on $line because the file() function does not get ride of the whitespace (usually a newline) after each line.
After the loop is complete, return the final array. Here is how my array looked, based off this data: SirAdrian, today, tomorrow
Bob, Jan 1st, Feb 1st
Jeff, Next Year, in two years Array
(
[0] => Array
(
[0] => SirAdrian
[1] => today
[2] => tomorrow
)
[1] => Array
(
[0] => Bob
[1] => Jan 1st
[2] => Feb 1st
)
[2] => Array
(
[0] => Jeff
[1] => Next Year
[2] => in two years
)
)
jwocky
05-31-2006, 01:33 AM
Awesome, thanks for the help
Looks like I'm thru step #2 with your help.
Now to learn how to properly access the data...
i'm trying to do
echo $users[0];
but that doesnt work obviously..
My next step is to do an if->then check with the data in my new array (thanks to adrian), how do I properly access the data to do that?
What I mean is what is the syntax to pull one piece of specific data out?
Thanks!
Kirk Y
05-31-2006, 01:37 AM
Do you mean like "$db->query(SELECT * FROM tablename WHERE condition = '1')"?
Adrian Schneider
05-31-2006, 01:38 AM
Foreach is very useful again (and in this case, all the loops are really)...
I would do something like this: // ...
$users = getNames();
foreach ($users as $user)
{
// $user[0] = username
// $user[1] = start date
// $user[2] = end date
}
Just a note, I changed my code earlier to a simplified version without updating the output, so check back on that. Anyway, inside this foreach loop, $user would be an array containing the user currently being processed. In here, you would display / do whatever you plan to do with each user.
Edit: echo $users[0] won't work because it is an array. It will just display "Array". To display anything, you will have to know the exact value you are searching for (or use a loop, which goes through them all). An example would be echo $users[0][0]; which would display the username of the first user.
@acidburn: the data is in a text file, so queries are no good here.
jwocky
05-31-2006, 01:50 AM
Foreach is very useful again (and in this case, all the loops are really)...
I would do something like this: // ...
$users = getNames();
foreach ($users as $user)
{
// $user[0] = username
// $user[1] = start date
// $user[2] = end date
}
Just a note, I changed my code earlier to a simplified version without updating the output, so check back on that. Anyway, inside this foreach loop, $user would be an array containing the user currently being processed. In here, you would display / do whatever you plan to do with each user.
Edit: echo $users[0] won't work because it is an array. It will just display "Array". To display anything, you will have to know the exact value you are searching for (or use a loop, which goes through them all). An example would be echo $users[0][0]; which would display the username of the first user.
@acidburn: the data is in a text file, so queries are no good here.
Adrian, absolutly perfect... this works perfectly for me. I'm still finishing up this section of my process, but basicly this is what i'm having it do, I'm having it take the first date (the start date) and compare it to today's date and my if->then statement combined with your foreach statement has done the trick so far.
FOr now I put in echo commands just to see if it was working so far, I will now replace those with the actual function I want it to do, which has to do with changing the usergroupd of that user... This will take a bit more research
function comparestart($today,$users)
{
foreach ($users as $user)
{
// $user[0] = username
// $user[1] = start date
// $user[2] = end date
/* echo $user[1]; */
echo "<BR>";
if ($today==$user[1])
{
echo $user[1];
}
}
}
Ok...
Now on to Stage 3..
Now that this script effectivly checks the user list from a text file and checks the date against todays date, it then effectivly sorts out whos date matchs todays date.
(note in previous posts I was using actual usernames, now I will swith to userid #s to keep things simple for now.. so $user is now a number instead of a name)
Now I have it calling a function called promote() which will take that user and move him from his current usergroup to usergroup #9
I'm using this call:
function promote($username)
{
$vbulletin->db->query_write("UPDATE user SET usergroupid = '" . "9" . "' WHERE userid IN (" . $username . ")"
echo $username . " promoted!!" . "<BR>";
}
Now this is not working.. I think the problem might not be just a problem with bad database query instructions, but a bigger problem. Namely, how in the world does this script know which database to use, etc etc.. Again i'm very new to this, so is there some initialization I should put at the begining of my php file to tell it what $vbulletin is, etc and where to find my databases?
sambah
05-31-2006, 02:55 AM
To tell your script which database to connect to do the following.
At the beginning of your file add:
include('config.php');
Then make a file called config.php while looks like this. (assuming your vb database is mysql)
<?
$dbuser = "your database username";
$dbpass = "your database password";
$db = mysql_connect("localhost", $dbuser, $dbpass);
$dbname = "name of your database";
?>
Hope this helps :)
jwocky
05-31-2006, 03:03 AM
Thanks very much, I will try that in a second.
I also found this:
require_once('./global.php');
does that do the same thing ?
sambah
05-31-2006, 03:08 AM
I'm not sure what global.php does to be honest but its not the DB configuration data.
The database file that VB uses is /includes/config.php :)
Sorry... I said invision before for some weird reason!
jwocky
05-31-2006, 03:23 AM
This is a great group of ppl we have here so far, I just took a break from coding for a few mins and I just realized how far I've gotten with this script from not knowing anything a few hours ago. I cant say thanks enought to everyone so far.. I cant wait to see this script to completion and hopefully others can use this info when starting their own scripts from scratch.
:banana: :banana:
sambah
05-31-2006, 03:36 AM
The SQL query you want is this:
UPDATE `user` SET `usergroupid` = '#' WHERE `userid` =x LIMIT 1 ;
# should be set to whatever usergroupid you want the person to be in
x = the user id of the member being promoted
I hope this is right :)
jwocky
05-31-2006, 04:01 AM
Ok using that info this is the php line I came up with:
$vbulletin->db->query_write("UPDATE user SET usergroupid = '9' WHERE userid = '" . $username . "' LIMIT 1");
$username actually defines the userid#.. i've checked to make sure its right through an $echo statement beforehand..
anyways, this just doesnt work, this is the error I keep getting..
Fatal error: Call to a member function on a non-object on line 37
sambah
05-31-2006, 04:34 AM
Try this.
$db_select = @mysql_select_db($dbname,$db) or die('Mysql Database Failure');
$table_name = 'user';
$result = mysql_query("UPDATE '.$table_name.' SET usergroupid = '9' WHERE userid = '" . $username . "' LIMIT 1") or die(mysql_error());
Sorry for the delay. I had to look that up on php.net :)
I only usually do databases, not PHP.
Let me know if it works :)
jwocky
05-31-2006, 04:40 AM
Interesting.. well now it atleast gives me this as output
"Mysql Database Failure"
so atleast your new code is doing something. I wonder what the issue is, I will tinker.
sambah
05-31-2006, 04:42 AM
Ok that means its not connecting to the DB.
Try removing the $db_select line and tell me what it says
jwocky
05-31-2006, 04:49 AM
UPDATE!!! SUCCESS!!! I removed ure previous code for conneting the database and thats why it didnt work, as soon as I put that code back in, it seems to now conenct to the database and made the neccesary change in the usergroup permissions!! this is awesome.
Thanks so much. Ok now onto the next phase of this project.
Actually before I move on, does anyone know if there is another way to conenct to the database since I dont feel right putting in my database name and password into this php file, altho I guess its ok if noone knows where it is right ?
sambah
05-31-2006, 04:51 AM
Yay! glad I could help a little!
You have the database details in the config.php file correct?
Adrian Schneider
05-31-2006, 04:51 AM
If you require() global.php, it basically "loads vBulletin". This gives you access to the database, user sessions, everything. Depending on what you are doing, you may or may not want this.
Anyway, whenever you see the database object ($db, $this->db, $vbulletin->db etc), it is going through vBulletin instead of just through MySQL. function comparestart($today, $users)
{
global $vbulletin;
foreach ($users as $user)
{
// Just to keep things clear
$userid = $user[0];
if ($today == $user[1])
{
promote($userid);
}
}
}
function promote($userid)
{
global $vbulletin;
$vbulletin->db->query_write("
UPDATE " . TABLE_PREFIX . "user
SET usergroupid = 9
WHERE userid = $userid
");
echo "User $userid promoted!<br />";
}
Not much new here... the global keyword gives $vbulletin global scope, I assume you know what that means because you are passing everything into the functions properly. You could also pass it (by reference! we don't want to duplicate it) into the functions if you prefer that method.
My query is essentially the same, too. I added the constant TABLE_PREFIX before the table name, which is pretty much the standard now so it will work no matter what your prefix is (or isn't). There is no need to put 9 in quotes, as it is an integer. Unless you have a list of userids, you can simply check if left side = right side.
sambah
05-31-2006, 05:00 AM
Aha. I was wondering what all the $db, $this->db, $vbulletin->db etc malarky was :)
jwocky
05-31-2006, 05:00 AM
Ah-ha. Yes so it can be done using vbulletins own variables. Wow we have 2 equally great solutions in one thread, will be great for ppl looking in at this thread in the future using search. Ok, now to Step 4, coding the opposite.. The demotions :)
Adrian Schneider
05-31-2006, 05:06 AM
Aha. I was wondering what all the $db, $this->db, $vbulletin->db etc malarky was :)
The main purpose of the database object is support for other databases without recoding all of the files. It also gives you fancy errors when you mess something up. Doing the same for each query using the native calls (mysql_query() for example), would require you to have to check it every time for failure.
Well... I'm out for the night, but I'll leave you guys with some good reads about vBulletin and how to write your own scripts using it...
Tutorial Index:
https://vborg.vbsupport.ru/showthread.php?t=99570
Pay special attention to these threads:
https://vborg.vbsupport.ru/showthread.php?t=98047
https://vborg.vbsupport.ru/showthread.php?t=98009
jwocky
05-31-2006, 12:13 PM
Ok I took a break from the coding this morning to add some more style to the script and this is what I discovered..
For the fellow newbies, if you want your script to output a page that looks just like a VB page this is what you want to start and end your script with:
Start:
<?php
// ####################### SET PHP ENVIRONMENT ###########################
error_reporting(E_ALL & ~E_NOTICE);
// ############# REQUIRE BACK-END #######################
require_once('./global.php');
// ############# Get Some templates #######################
eval('$headinclude = "' . fetch_template('headinclude') . '";');
eval('$header = "' . fetch_template('header') . '";');
eval('$navbar = "' . fetch_template('navbar') . '";');
// ############# Output templates #######################
echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">';
echo '<html dir="' . $stylevar[textdirection] . '" lang="' . $stylevar[languagecode] . '">';
echo $headinclude;
echo '<body>';
echo $header;
echo $navbar;
End:
echo $footer;
echo '</body>';
echo '</html>';
?>
And now onto Step 5.
I'm right now trying to add some functionality to the script, I want it to be able to go and edit the .txt file I have and be able to add data, remove data, or edit data from it.
Again for me its the basis, so I'm just trying to get a function call to work only if I hit a button. I can make the button appear and will probably do this by making a form that posts to itself.
My question is, is there a way to load a .php file and have it go straight to running a certain function?
Is that what the "?" in the filename is for?.. Ie. I see some scripts running like this: "https://vborg.vbsupport.ru/newreply.php?do=newreply&noquote=1&p=993576" Is the newreply.php?do=newreply telling the newreply.php to only run the newreply function ?
I tried to do something similar on my own script but that obviously didn't work yet :)
UPDATE
I've found the answer to that..
What I did was look at vb's own file and just copied what they did from their login script, this is how I got it working
I added this into my main body of the script:
echo 'Run Update on users <FORM ACTION="main.php" METHOD="POST"><input type="hidden" name="do" value="start"><INPUT align=center TYPE="submit" VALUE="Vendor Access">
then I put in this if statement later on
if ($_POST['do'] == 'start')
{
start();
}
So basicly what thats saying is If someone enters this page with a value of "do=start" then I want it to run the start();
And of course my button in an above section would labeled name="do" value="start"
So all is well so far, steaming right along! Now to get my user.txt data to post itself in a nice form which would let you edit it. I think this will be hard as hell, but here I go...!
UPDATE #2
Ok so now i'm writing the section of this program that will find the given user's threads (threads started by him) in a predefinied forum and then close the thread.
This is what I have so far:
$prevarts = $vbulletin->db->query_read("SELECT threadid, title, forumid FROM " . TABLE_PREFIX . "thread WHERE postuserid = $userid AND forumid IN ($mrkt) ORDER BY dateline DESC");
while ($titles = $vbulletin->db->fetch_array($prevarts))
{
$tit = $titles['title'];
$tid = $titles['threadid'];
$fid = $titles['forumid'];
echo "<BR>Closing thread: <b>" . $tit;
}
now the echo does spit out the threads started by that user, however it is doing it in more then the predefined forumid. What its including are threads that were started in that definied forumid but later moved to another forum. Is there a way to refine the SELECT call to pick out only the threads that are still currently in that forumid ?
Also does anyone have a clue how to move from here to tell the DB to close that given thread ?
Thanks!!
UPDATE #3
Took me a while, but I got the script to close the threads one by one by using this bit of code.. woohoo!
/* CLOSE THREAD */
$prevarts = $vbulletin->db->query_read("SELECT threadid, title, forumid FROM " . TABLE_PREFIX . "thread WHERE postuserid = $userid AND forumid IN ($mrkt) ORDER BY dateline DESC");
while ($titles = $vbulletin->db->fetch_array($prevarts))
{
$tit = $titles['title'];
$tid = $titles['threadid'];
$fid = $titles['forumid'];
echo "Closing thread: <b>" . $tit . "</b>";
echo "<BR>";
$vbulletin->db->query_write("UPDATE " . TABLE_PREFIX . "thread SET open = 0 WHERE threadid = '$tid'");
}
/* END CLOSE THREAD */
$mrkt is a predefined forumid# since i want this to only effect one of my forums, not the entire place.
Adrian Schneider
06-05-2006, 02:15 AM
Did you figure everything out? The "?" and everything after it in the URL, is known as the query string. It can be accessed via the $_GET array. file.php?do=closethread&t=15 will translate into the following $_GET array:
$_GET = array(
'do' => 'closethread',
't' => 15
);Because there values can be easily tainted, if you are using them in queries (or even at all) you should make sure they are "clean". The threads I posted above should show you how to use vBulletin's cleaning functions to make them safe. Once this is done, they all go into $vbulletin->GPC.
$_POST is similar, it is populated from form elements where the form action is set to post. These are just as insecure as $_GET values, so be sure to clean them.
Also, it might be easier to use a template instead of using echo and basically recreating the standard shell template (GENERIC_SHELL).
vBulletin® v3.8.12 by vBS, Copyright ©2000-2025, vBulletin Solutions Inc.