• This forum has been archived. New threads and replies may not be made. All add-ons/resources that are active should be migrated to the Resource Manager. See this thread for more information.

[part 3] Creating a add-on to insert tabs in profile page (using hooks)

Status
Not open for further replies.

Fuhrmann

Well-known member
Hey guys. I hope the last tutorial was not to hard.

FIRST PART HERE
SECOND PART HERE

In this tutorial we'll be making something more complex. We will be using more PHP code and go throug the XenForo's code.

So before we start, i really recomend you guys to use some IDE for programming in PHP. There is a tutorial made by Kier and you can see it HERE

So, your PHP EDITOR (or something) is ready? Let's go then.

Step 1 - Defining our mission

Remember in the previous tutorial that we put some new info in our content tab? We've used the Custom User Fields to make a field called "Favorite Band" and put in our content tab.
We also learn about conditionals and some other things.

Now, let's navigate throug the files of Xenforo so we can make what we want.

This tutorial will teach you how to put all likes that user gave to other users in his profile page, in our tab.

So all the tabs will make a group, like this:

Profile Page
---Profile Posts
---Recent Activity
---Postings
---Information
---Our Tab

This tutorial use some knowlogde of MVC structure, php code and other things.

Step 2 - How we will do that?

So, first of all, you have to know that not always we need to reinvent the wheel.

"I know, Fuhrmann, go to the point!".

Sure. What i am saying is maybe in some XenForo's files can exist a method that list all the user's likes that he gave. Dont you think? Yes. I always think this way. So the first thing i do, is looking for a method that already do what i want. So i dont need to make any calls to the database, i just have to use the right function.

In Xenforo structure (library/XenForo we have so many folders/files, see:

1.webp

XenForo use the MVC structure:

Model: In this layer are defined rules of access and manipulation of data, which often stored in databases or files, but nothing indicates that serve accommodation only for persistent data. Can be used for data in volatile memory, eg RAM, although such use does not happen very often. All rules related to treatment, obtaining and validating data must be implemented in this layer.

View: This layer is responsible for generating the way the answer is displayed, web page, form, report, etc..

Controller: It is the layer responsible for responding to requests by the user. Whenever a user makes a request to the server layer is the first to be executed.

1. The user makes a request to the server.
2. The controller processes the user request.
3. The controller makes a call to the model, to retrieve or manipulate data.
4. The model returns the requested data will be passed to the view.
5. The view generates the presentation of data, eg an HTML page.

This explanation is just a resume. The purpose of the tutorial is not to get deeper in the MVC.

Well, lets see, in Model we have this:

...rules of access and manipulation of data...

So, this is what we want! A method that gonna call all user's likes that he gave with a query to the database. But, what doest that mean?

This mean something...

Look:

In the XenForo folder (library/XenForo) we have this structure:

--AdminSearchHandler
--AlertHandler
--AttachmentHandler
____OTHER FOLDERS____
--Importer
--Install
--LikeHandler
--Model
....

See? There is the model folder. All the model files are stored in there. So, thats a tip that our method should be in there. Go into the folder Model.

You will se a lot of more files and files. So confusing! But not to much. Think, we want a method that select all the user's likes. Let's look for some file called Like and see what we find.

2.webp

Yes, there is a file called Like.php!

Open this file.

We see a bunch of methods and comments. That's good, will help us. So, looking for what we want, i found something:

PHP:
/**
* Gets likes based on the content user.
*
* @param integer $userId
* @param array $fetchOptions Fetch options. Supports limit only now.
*
* @return array Format: [like id] => info
*/
public function getLikesForContentUser($userId, array $fetchOptions = array())
{
$limitOptions = $this->prepareLimitFetchOptions($fetchOptions);

return $this->fetchAllKeyed($this->limitQueryResults(
'
SELECT liked_content.*,
user.*
FROM xf_liked_content AS liked_content
INNER JOIN xf_user AS user ON (user.user_id = liked_content.like_user_id)
WHERE liked_content.content_user_id = ?
ORDER BY liked_content.like_date DESC
', $limitOptions['limit'], $limitOptions['offset']
), 'like_id', $userId);
}

But...no, wait! Oh, ********!

I think that's not what we want. See below, there is a example of the structure of the database table xf_liked_content that holds all the likes given:

like_idcontent_type = 1
content_id = post
like_user_id = 2
like_date= 1318342341
content_user_id = 1

like_user_id is the ID of the user who actually LIKED the content.
content_user_id is the ID of the user who actually RECEIVED the like.

We want to get all the likes were GIVEN by user_x. The method above, select all the likes of the user who received the likes. We want the givers.

(I just wanna make sure that's obvious other MANY ways to look files throug in XenForo. The way i teach, is the way i like, started. So, you can use your own way to find methods and other things inside the files of XenForo.)

Step 3 - the Model - Like

As we know, in the Model we "defined rules of access and manipulation of data, which often stored in databases or files". But if we want to create our own methods to manipulate data in XenForo? Customizable methods?

Well, then we have to extend the Model or create one for our add-on. In this case, we gonna create one.

Dont need to extend, because we wont use any parent method (Thanks to point me out ragtek!)

To do that, lets create the file that will be responsable for the model.

Remember the structure of folders and files we have so far? This:

library
-- newProfileTabs
----- Listener.php

Now let's create one more file, called newProfileModel.php, because we'll create a model for our addon with own method. I always follow a pattern of folders and filenames, so this is how i use and what you got if you follow my directions:

library
-- newProfileTabs
----- Model (new folder)
---------- newProfileModel.php (our new file!)
----- Listener.php

Open our new file (newProfileModel.php). We need to give it a class name. So, as always, we follow the directories structure. So our file newProfileModel.php will be:

PHP:
<?php
class newProfileTabs_Model_newProfileModel extends XenForo_Model
{
	
}
?>

We are extending to XenForo_Model because we are creating a Model. That way we can acess any other functions of the base class Model

We will know create our method to get the data from data base and return all we want: all the like that user gave.

So, our final version of file newProfileModel.php will be this:


PHP:
<?php
class newProfileTabs_Model_newProfileModel extends XenForo_Model
{
	public function getAllGivenLikesByUser ($userId, $howMuch)
	{	
		$db = $this->_getDb();
	
		//Query the database with what we wat
		return $this->fetchAllKeyed('
				SELECT 
					liked_content.*,
					user.username as receive_username,
					post.*
				FROM xf_liked_content AS liked_content
				INNER JOIN xf_user AS user ON (user.user_id = liked_content.content_user_id)
				LEFT JOIN xf_post AS post ON (post.post_id = liked_content.content_id)
				WHERE liked_content.like_user_id = ?
				ORDER BY liked_content.like_date DESC
			', 'like_id', $userId);
	}
}
?>

I will not explain the SQL query, assuming that you already have a base of it.

So, there is our function. What this do?

- Select all fields from the xf_liked_content table WHERE the like_user_id field is equal what we pass throug the method.
- Also, select username field from xf_user table where the user_id field is equal to content_user_id field.
- Also select post.* for us to show the post of the user who received the like and some other fields.

Save, and let open. Now we will extend other file. The controlller.
 
Step 4 - The Controller Public - Member

Remember the exaplanation about controllers?

Controller: It is the layer responsible for responding to requests by the user. Whenever a user makes a request to the server layer is the first to be executed.

So, lets explain that.

What we want to do is:
-- Go to profile page (index.php?members/fuhrmann.1)
-- Click in our Tab (index.php?members/fuhrmann.1/#ourTab)
-- See all the likes that user_x gave.

As you know, we have used "members/fuhrmann.1/#ourTab" to access the content of our tab.

So:

controller (Route Prefix)/user_name.user_id/action

In this case "#ourTab" is not a action, because we did'nt define any action in the controller.

Don't know if you remember, but the content of our tab is (newProfileTab_ourTab_content):

Code:
<li id="ourTab" class="profileContent">
<div class="section">This is our custom user field:</div>
<dl class="pairsInline userField_favoriteBand">
<dt>{xen:helper userFieldTitle, favoriteBand}:</dt>
<dd>{$user.customFields.favoriteBand}</dd>
</dl>
</li>

So, we load a custom user field. What we'll do know is define a action in the parent controller, and do what we want: show all user's like. We need to know what controller are we working and how we will use it. As i said, the controller we are using is "members".

We'll then extend the Controller "members". To find that, just go to the folder "ControllerPublic" located in \library\XenForo\ControllerPublic. There is a file Member.php. Open.

Are you seeing the actions?

- actionFollowing()
- actionFollowers()
- actionFollow()
- actionUnfollow() and others.

So, lets do a test, so we can understand. Replace the "{user_name}" with the name of the user you are seeing the profile.

Navigate to:

index.php?members/{user_name}/Following
index.php?members/{user_name}/Followers
index.php?members/{user_name}/Follow
index.php?members/{user_name}/Unfollow

BOLD - The action.

See the pattern here? All the actions i listed here, you can acces in the browser just navigating. But this not means all the actions are the same thing.

I think we got what we have to do, right?

This:

index.php?members/{user_name}/UsersLike

BOLD - Our new action, to do what we want. Get all the like that user_x gave.

So, what we know:

- File to extend - Members.php - Controller Public
-- Action to create - UsersLike - actionUsersLike()

Step 5 - Extending the Controller Public - Member

First, create the file follow the structure directories. We'll have this:

library
-- newProfileTabs
----- Extend
-------- ControllerPublic (our new folder)
------------ Member.php (our new file!)
------Model
-------- newProfileModel.php
----- Listener.php

Open Member.php and lets starting editing.
As i've told you, we'll extend the Controller Member. As normal, we make something like that:

PHP:
<?php
class newProfileTabs_Extend_ControllerPublic_Member
{

}
?>

Kier said:
But for a dynamic system where there might be dozens of classes attempting to extend the like model, this is unworkable.

Instead, we must make use of the XenForo Class Proxy system, which allows the system to effectively have multiple inheritance capabilities. To use it, we must declare the class as ClassName, and have it extend XFCP_ClassName, like this:

PHP:
<?php
class newProfileTabs_Extend_ControllerPublic_Member extends XFCP_newProfileTabs_Extend_ControllerPublic_Member
{

}
?>

Ok. Now we have a new file, a new class extending..... it self?? Oh! Wait. As Kier explaining above, this is the XenForo Class Proxy system (XFCP), that "allows the system to effectively have multiple inheritance capabilities", so dont worry.

- File to extend - Members.php - Controller Public --- DONE!
-- Action to create UsersLike - actionUsersLike() -- WILL DO NOW

Now, let's create our action:

PHP:
<?php
class newProfileTabs_Extend_ControllerPublic_Member extends XFCP_newProfileTabs_Extend_ControllerPublic_Member
{
//See? actionNameOfOurAction
public function actionUsersLike()
    {
//Lets get the userID
        $userId = $this->_input->filterSingle('user_id', XenForo_Input::UINT);
$user = $this->getHelper('UserProfile')->assertUserProfileValidAndViewable($userId);

//Lets get all the likes that this user gave to anothers
//We are using our function here and our model! getAllGivenLikesByUser()
$users_likes = $this->getModelFromCache('newProfileTabs_Model_newProfileModel')->getAllGivenLikesByUser($userId, '10');

//Returning all the values to our template newProfileTab_ourTab_content
//With the variable $users_likes, and the variable $user.
        return $this->responseView(
            'newProfileTab_ViewPublic_Member_UsersLike', 'newProfileTab_ourTab_content',
array('user' => $user, 'users_likes' => $users_likes)
        );
    }
}
?>

Its done. But we dont know if it works. And i can say, dont work.
There is more things to do.

Remember the Code Event Listeners? Yeah. We need to create them so our extending classes get to work.
 
Step 6 - Code Event Listeners

I already made a tutorial explaning how Code Event Listeners works, so i am not going to explain this time. But it's not to dificult.

- Create a new Event Listener.
- In Listen to Event chose load_class_controller (we have extended the Controller Member, remember?)
- In Execute Callback do not fill yet.
- Chose the Add-on "New Profile Tabs".


Now, BEFORE saving it.

Open your file Listener.php and add the following code:

PHP:
public static function load_controller ($class, array &$extend)
{
	//We extend this, remember? The member controller.
	if ($class == 'XenForo_ControllerPublic_Member') 
	{
		//We created this class, remember? So that goes here.
		$extend[] = 'newProfileTabs_Extend_ControllerPublic_Member';
	}
}

Get back to the page of Code Event Listeners and fill with this:

Execute Callback: First field is the name of the class of our listener (Listener.php) which is newProfileTabs_Listener. The second field is the name of our method that we created right now, that is: load_controller.

Hit Save Event Listener.

All required Event Listeners to work with our add-on is done. Next Step.

Step 7 - Changing our template content

We have made the core until now.
Created the method to get all the likes that user_X gave and get the array as return.

We can now use all the parameters that our method grabs it on our newProfileTab_ourTab_content.

So, open the template newProfileTab_ourTab_content. This is what you got:

Code:
<li id="ourTab" class="profileContent">
<div class="section">This is our custom user field:</div>
<dl class="pairsInline userField_favoriteBand">
<dt>{xen:helper userFieldTitle, favoriteBand}:</dt>
<dd>{$user.customFields.favoriteBand}</dd>
</dl>
</li>

We will now change all the code to fit our necessities. So, what we want to show in here? A phrase? A name. Let's define.

As we know, we are able to use rows from three table: xf_post, xf_user and xf_liked_content.

This is what will be our final version of the template:

3.webp

So, still with the template newProfileTab_ourTab_content open, lets replace all the code for this:

Code:
<xen:require css="search_results.css" />
<li id="ourTab" class="profileContent" data-loadUrl="{xen:link members/userslike, $user}">
<xen:if is="{$users_likes}">
<xen:foreach loop="$users_likes" value="$like">
<ol>
<li class="searchResult post primaryContent">
<div class="listBlock main">
<div class="titleText">
<h3 class="title"><a href="posts/{$like.post_id}/">{$user.username} liked the post of {$like.receive_username}:</a></h3>
</div>
<blockquote class="snippet">
{xen:helper snippet, $like.message, 150}
</blockquote>
<div class="meta">
Post by: <xen:username user="{$like}" rich="true" />,
Liked in <xen:datetime time="{$liked_content.like_date}" />
</div>
</div>
</li>
</ol>
</xen:foreach>
<xen:else />
<p>This user not liked any content yet.</p>
</xen:if>
</li>

Explanation:

Code:
<li id="ourTab" class="profileContent" data-loadUrl="{xen:link members/userslike, $user}">
The data-LoadUrl is used to load when we need to load more then a simple content. So we are using!


Code:
<xen:require css="search_results.css" />

This is the CSS used to display search results. Since this is a tutorial on how to make a simple addon, i just grab a css and put here to make some style in our result. But for sure, you can develop your own style of display.

Code:
<xen:if is="{$users_likes}">

Code:
<xen:else />
<p>This user not liked any content yet.</p>
</xen:if>

Is the user really have give some like in content until now, we will show it. If not, we will show that message above.

[CODE[<xen:foreach loop="$users_likes" value="$like">[/CODE]

Since we are getting more then one row from the database, its usual that returns me a array. So we use a "xen:foreach" to loop throug the values and print them.

Code:
<h3 class="title"><a href="posts/{$like.post_id}/">{$user.username} liked the post of {$like.receive_username}:</a></h3>

$user is the own of the profile page. $like.receive_username is the user who RECEIVES the like. How i selected this field? Here, in the Member Controller that we created together:

Code:
SELECT liked_content.*, user.username as receive_username,

Code:
{xen:helper snippet, $like.message, 150}

Here we are using the xen helper. I will not teach anything about it, but i have to say that's very usefull. Here i just call the helper, pass the post message to the helper and how many caracters i want to show. Its a snippet.

Code:
Post by: <xen:username user="{$like}" rich="true" />,

With this we can show the user name with a link. Its simple. Just pass a array and select if you want rich or not.

Code:
Liked in <xen:datetime time="{$liked_content.like_date}" />

And, this is the time. When we get them from the database is is another format (1318262027), but with the xen:datetime, we got it with a readable human time.

So thats it.

4.webp
 
Another great tutorial, however won't the:
PHP:
XenForo_Model_Like::getLikesForContentUser($userId, array $fetchOptions = array())
function work for you?

Here's the docblock:
/**
* Gets likes based on the content user.
*
* @param integer $userId
* @param array $fetchOptions Fetch options. Supports limit only now.
*
* @return array Format: [like id] => info
*/
 
Another great tutorial, however won't the:
PHP:
XenForo_Model_Like::getLikesForContentUser($userId, array $fetchOptions = array())
function work for you?

Here's the docblock:

Thanks!

About the question:

I dont think...

Because i dont want to get the content user. I want to get the user who gave the like...
And in this method search for the content user id. The user who receive the like:

WHERE liked_content.content_user_id = ?

Maybe i can be wrong, but that's what i think.
 
Hi Fuhrmann,

Just went through this and got it working! Its amazing how much easier it is to start understanding when your actually doing it then just reading it!

There are a couple of bits, which I was hoping you might just be able to explain if you have the time?

In Step 3, this line
PHP:
 public function getAllGivenLikesByUser ($userId, $howMuch)

Did you create the "getAllFivenLikesByUser" name? Also, did you create the $howMuch variable? What is the $howMuch used for as I cant see it mentioned elsewhere in the tutorial?

I know you said
I will not explain the SQL query, assuming that you already have a base of it.
Reading through my book, I have a basic grasp of sql, I was wondering if you had the time if you could just break this down line by line?

PHP:
//Query the database with what we wat
        return $this->fetchAllKeyed('
                SELECT
                    liked_content.*,
                    user.username as receive_username,
                    post.*
                FROM xf_liked_content AS liked_content
                INNER JOIN xf_user AS user ON (user.user_id = liked_content.content_user_id)
                LEFT JOIN xf_post AS post ON (post.post_id = liked_content.content_id)
                WHERE liked_content.like_user_id = ?
                ORDER BY liked_content.like_date DESC
            ', 'like_id', $userId);

In Step 5 where we created this:

PHP:
//See? actionNameOfOurAction
public function actionUsersLike()
    {
//Lets get the userID
        $userId = $this->_input->filterSingle('user_id', XenForo_Input::UINT);
$user = $this->getHelper('UserProfile')->assertUserProfileValidAndViewable($userId);

I dont quite get it. Is this basic PHP5, which I will understand when my book arrives, or is this exclusive to xenforo (Parts such as "filterSingle")

If not, bits of code such as "getHelper", "UserProfileValidAndViewable" etc - Where abouts can I read up on them and what they do?

Sorry to sound pain! I follow the logic ofthe majority of the add-on, its just when bits of code spring up and I dont know where they have come from, what they do or how to find out about them I get confused!

Might have a few questions at some point about the code in the template! just reading it over and over and having a play to see if I can work it out first!

Once again though, thanks for a great tutorial. Really looking forward to when I have a better understanding and can start coding my own add-ons!
 
Hello DaveL!

So much appreciate that you are making progress!

Did you create the "getAllFivenLikesByUser" name?

The "getAllGivenLikesByUser" is the name of the method i created yes. I just choose this name because its more easy to understand what we are doing.

Also, did you create the $howMuch variable? What is the $howMuch used for as I cant see it mentioned elsewhere in the tutorial?

I have to say, just now that i saw that. I completly forgot to use this variable. This var would be usefull to us, to limit the returned rows in our method. So, we could be using like this:

"getAllGivenLikesByUser" (user id is 1, i want just 15 likes)

But i forgot to put the $howMuch variable in the method. Really sorry.

Reading through my book, I have a basic grasp of sql, I was wondering if you had the time if you could just break this down line by line?

Yes! :)

Code:
$this->

Our object. That's is what we are using. THis is some basic of OOP.

Code:
fetchAllKeyed()

As is documented in the file library/XenForo/Model.php:

Fetches results from the database with each row keyed according to preference.
The 'key' parameter provides the column name with which to key the result.
For example, calling fetchAllKeyed('SELECT item_id, title, date FROM table', 'item_id')
would result in an array keyed by item_id:
[$itemId] => array('item_id' => $itemId, 'title' => $title, 'date' => $date)*

Code:
SELECT liked_content.*,  user.username as receive_username,  post.*

This is what we want to select. Which fields we want to select in the database so we can use them later. So, if i use: "liked_content.*" i want all fields of the table liked_content. But you may say: "liked_content is not a table!".

See this line:

Code:
xf_liked_content AS liked_content

This is a alias. We make liked_content a alias for xf_liked_content.

Code:
FROM xf_liked_content AS liked_content

This line we tell where we want to select the main data.

Code:
INNER JOIN xf_user AS user ON (user.user_id = liked_content.content_user_id)

Here we make a INNER JOIN. INNER JOIN is used to get data from other table (short explain), its a JOIN used with a ON conditional. So, we want to JOIN the table xf_user, because in our output we want to put some data of our user. So, if

we want to use the user username, we have to select it, right?

But, we just want to select the user who receive the LIKE. Because the fields from the user who gave we already own. So, we make this:

An INNER JOIN will retrieve all records from both tables that have matching values for whatever column you're joining them on.

Code:
ON (user.user_id = liked_content.content_user_id)

Code:
LEFT JOIN xf_post AS post ON (post.post_id = liked_content.content_id)

Here we want to JOIN too. We want to select the data from the xf_post table, but we ONLY want the post who has the same post_id that the user gave a like. This way, if you get a look in the xf_post we can use the field "message" to show

the content of the post.

A LEFT JOIN will retrieve all records from the left (first) table, and only the records from the second table with matching values for the joining column, filling in any empty spaces with NULL values.

PHP:
public function actionUsersLike()

This is just the name of our action.

PHP:
$userId = $this->_input->filterSingle('user_id', XenForo_Input::UINT);

Example:

We have a form. The form has two input fields:

PHP:
<input type="text" name="user_id" value="test">
<input type="text" name="post_id" value="test">

We want to make a action that get the values from this form. So we use this:

PHP:
$userId = $this->_input->filterSingle('user_id', XenForo_Input::UINT);

Which 'user_id' is the name of your input. This is just a example. If you want to know more, take a look at the file library/XenForo/Input.php in the line 102.

Code:
XenForo_Input::UINT

This is a contant value for the input type. You can see in the file i tell you above:

PHP:
const STRING    = 'string';
const NUM        = 'num';
const UNUM      = 'unum';
const INT        = 'int';
const UINT      = 'uint';
const FLOAT      = 'float';
const BINARY    = 'binary';
const ARRAY_SIMPLE = 'array_simple';
const JSON_ARRAY = 'json_array';
const DATE_TIME      = 'dateTime';

PHP:
$this->getHelper

As explained in the file library/XenForo/Controller.php:

Creates the specified helper class. If no underscore is present in the class name, "XenForo_ControllerHelper_" is prefixed. Otherwise, a full class name is assumed.

Helper class, as the name says, its a class with some functions to help you in some actions, methods (shot explain)

Code:
getHelper('UserProfile')

This is the helper we want to get.
You can see in the file library/XenForo/ControllerHelper/UserProfile.php

Here is the explanation in the file:

* Helper for user profile related pages.

PHP:
assertUserProfileValidAndViewable($userId);

This is the function we want to call in the helper UserProfile. This functino is in file library/XenForo/ControllerHelper/UserProfile.php. You can see for yourself in the line 26.

Might have a few questions at some point about the code in the template! just reading it over and over and having a play to see if I can work it out first!

You can ask, maybe i can help in this too. :)
 
Hi Fuhrmann,

Many thanks for going through that :) Ive just had a quick look because im about to head out to work, but it all seems to be making sense now! Will sit down tomorrow and read/digest it all a bit better.

The SQL you explained has now really helped. What I read in my book a few years ago didnt go into that much detail.

Still dont quite understand the helper part yet. I seem to understand controllers/models etc. Is helper part of OOP or just xenforo? If OOP I will have a google up on it tomorrow.

Can I also ask how you got to know where to look for everything? Was it just a case of browsing around or did you have previous experience with OOP?

Also, I dont know if you planned to create any more tutorials, but another interesting one based on the profile tab with the permissions you went through would be a "member notes" kind of add on.
Usergroups with permissions (admins/mods) can only see the tab on users profiles. Each tab has a text input box similar to that of the "profile posts" tab. Admins/Mods could then post a note which would only show to other Admins/Mods underneath the input box.
 
Glad i could help you.

The "helper" is part of the MVC structure. XenForo has various helpers you can use. You can see this link, maybe help you: http://weblogs.asp.net/jgalloway/ar...ion-methods-and-declarative-razor-helper.aspx

Can I also ask how you got to know where to look for everything? Was it just a case of browsing around or did you have previous experience with OOP?

Well...i have a little previous experience with OOP is with Visual Basic .NET, JAVA and PHP. I started learning the MVC structure when i got my XenForo license...but, still learning.

I think the best way to know 'where to look' is use some php IDE. I use Zend Studio. It's very helpfull. I just search what i want throug the files of XenForo and take a look.
There is a bunch of comment in each function.

Also, I dont know if you planned to create any more tutorials,

Yes, i wil! :)

but another interesting one based on the profile tab with the permissions you went through would be a "member notes" kind of add on.
Usergroups with permissions (admins/mods) can only see the tab on users profiles. Each tab has a text input box similar to that of the "profile posts" tab. Admins/Mods could then post a note which would only show to other Admins/Mods underneath the input box.

That's a really good for a tutorial! Since we already have a PART 1, explaining how to put the tab in the profile page, just have to make some other tutorial explaining the functtionality.

Any doubts, just ask!
 
Just finished this tutorial, there's much more to go through here, but... I'm getting it! I need to dig into things a little more and make sure I understand some of the core concepts here, but... overall, very good and thank you!!
 
Just finished this tutorial, there's much more to go through here, but... I'm getting it! I need to dig into things a little more and make sure I understand some of the core concepts here, but... overall, very good and thank you!!


Good to know! Any questions, post in here! :)
 
Status
Not open for further replies.
Top Bottom