Let's see how long I can go. :P
One day, one function (or feature) explained, along with examples on how to use it in addons.
This is both a way of document more in detail functions and a way to "test" their real usability level (if I can't explain them, or you don't understand them, it means they are broken).
I'll use just one topic not to spread informations around, feel free to ask questions, post feedback or post your own function of the day if you want or I miss a day. ;)
List of functions- addInlineJavascript (http://www.elkarte.net/community/index.php?topic=831.msg6004#msg6004)
- addMembersToGroup (http://www.elkarte.net/community/index.php?topic=831.msg5942#msg5942)
- createList - part 1 (http://www.elkarte.net/community/index.php?topic=831.msg5830#msg5830)
- database (http://www.elkarte.net/community/index.php?topic=831.msg5848#msg5848)
- determineAvatar (http://www.elkarte.net/community/index.php?topic=831.msg11343#msg11343)
- elk_array_insert (http://www.elkarte.net/community/index.php?topic=831.msg5805#msg5805)
- loadCSSFile (http://www.elkarte.net/community/index.php?topic=831.msg5956#msg5956)
- loadJavascriptFile (http://www.elkarte.net/community/index.php?topic=831.msg5984#msg5984)
- template_select_boards (http://www.elkarte.net/community/index.php?topic=831.msg5944#msg5944)
Today I'm a bit busy, so a very simple one: database.
This function does one single thing in only one way: it retrieves and return a database object. Stop.
Example:
$db = database();
done.
In the old SMF world, there was the famous $smcFunc to add to the global declaration and then use things like
$smcFunc['db_query'](blabla)
in Elk's world the way to use the database is:
// retrieve the db object
$db = database();
// use it
$db->query(blabla);
query and the rest of the db layer are for another day.
Today I'm quite late... sorry, I lost a day (I thought it was Sunday...)
Today's function is an administrative one that may become handy for addons: addMembersToGroup
Let's see what the documentation says (that way we can also start to familiarize with docBlocks:
* Add one or more members to a membergroup.
*
* Requires the manage_membergroups permission.
* Function has protection against adding members to implicit groups.
* Non-admins cannot add members to the admin group, or protected groups.
*
* @param string|array $members
* @param int $group
* @param string $type = 'auto' specifies whether the group is added as primary or as additional group.
* Supported types:
* - only_primary - Assigns a membergroup as primary membergroup, but only
* if a member has not yet a primary membergroup assigned,
* unless the member is already part of the membergroup.
* - only_additional - Assigns a membergroup to the additional membergroups,
* unless the member is already part of the membergroup.
* - force_primary - Assigns a membergroup as primary membergroup no matter
* what the previous primary membergroup was.
* - auto - Assigns a membergroup to the primary group if it's still
* available. If not, assign it to the additional group.
* @param bool $permissionCheckDone = false if true, it checks permission of the current user to add groups ('manage_membergroups')
*
* @return boolean success or failure
The function resides in Membergroups.subs.php, as written in the documentation its basic function is just add one or more members to a specific member group.
The basic usage is rather simple:
addMembersToGroup(10, 1);
This code adds the member with ID 10 to the group 1 (the administrators group). It will also "decides" whether the group will be primary or additional all by itself. This provided that the user "running" the function is allowed to change membergroups.
Then of course few parameters can be specified in order to better tune the usage, for example you can specify an array of members, or if the group should be primary or additional, and if you want to override the permissions.
This last part (about permissions) is nice, because for example we could use it to change a membergroup of a member based on some action (for example a subscription <= hit we are not yet doing it, but we are using custom queries! :P), even if the member is technically not allowed to change groups.
So, let's say we create a kind of reputation system that when a member reach 100 points he is inserted automatically into a special group, we could write:
require_once(SUBSDIR . '/Membergroups.subs.php');
addMembersToGroup($user, 15, 'only_additional', true);
Done. the function will take care of move the user $user to the group 15, making it an additional group, and without checking permissions, because the code would run at "any time", so the user triggering that function could not be an admin (or alike).
After about 8 months, I'm back! :P
Today's function is determineAvatar.
Find the avatar of a user
may look like a simple thing, but it's not.
ElkArte allows for several different type of avatars:
- Uploaded avatars as attachments,
- Uploaded avatar in a custom directory,
- External URL,
- Default avatar,
- Gravatar
I may have missed some combination. lol
So, in order to fine a member's avatar the common operation is a query that JOINs the attachments table on the member's ID and the result are 5 values that have to be combined together in order to obtain a single URL. Not something trivial.
Additionally, there is some legacy stuff for avatar resizing that is still around, and should be taken into account.
The function I'm presenting today, does exactly that: provided an array with these 5 "elements" it returns the url to the member's avatar.
The 5 values are the following database fields ({table}.field):
- {members}.avatar - can be an url or the type of avatar
- {members}.email_address - used for the gravatars
- {attachments}.id_attach - of course the id of an attachment that should represent the member avatar if saved as attachment
- {attachments}.attachment_type - type of attachment, if empty means avatars as attachment, otherwise it means the avatars are stored in another directory and have their own URL,
- {attachments}.filename - the name of the file (of the avatar obviously :P).
So, a query to grab what you need would require at least something like this:
SELECT
[..]
mem.avatar, mem.email_address,
IFNULL(a.id_attach, 0) AS id_attach, a.filename, a.attachment_type
FROM [...]
LEFT JOIN {db_prefix}members AS mem ON (mtn.id_member_from = mem.id_member)
LEFT JOIN {db_prefix}attachments AS a ON (a.id_member = mem.id_member)
The result of this query (fetched with fetch_assoc) would be an array containing the values of the fields, let's say $row.
Use determine Avatar is as easy as to call it:
$avatar_url = determineAvatar($row);
That's all you have to do.
determineAvatar returns an array of 5 element:
$avatar = array(
'name' => '',
'image' => '',
'href' => '',
'url' => ''
'gravatar_preview' => ''
);
image is the img tag.
href and url are almost exactly the same, except that href is always filled, while url doesn't contain anything in certain cases.
gravatar_preview is always present, even if there is no avatar set.
That's a quick description, though I hope it may help!
This is a slightly more "wide" explanation for today.
Recently I introduced a couple new ways to expand ElkArte that will make their appearance starting from version 1.1, but I wanted to start introducing them now, because they give a bit more power to coders that want to write addons.
These "things" are not really finalized yet, they may change before the final release, but the general idea is present, so today I'll start with that and with hopefully a couple of example to show how to use them.
The first thing I wanted to talk about are modules, but then I realized that modules can express their full potential only using events, so I decided to take a step back and start with events.
Since have a working example to read is important, I'll use the calendar code that has been
extracted from the core and converted to a module in the development branch.
Starting from Elk 1.1, each controller will be able to "fire" events. At first sight, an event is not much different from a hook, let's have a look at the BoardIndex_Controller class, we will find something like:
$this->_events->trigger('pre_load', array('boardIndexOptions' => &$boardIndexOptions));
so, an identifier "pre_load", and an array of variables, similar to what you see for a random hook:
call_integration_hook('integrate_validateSession', array(&$types));
So, why did I introduce events? Because I'm mad... oh, no way that was supposed to be in my head. :P
No, there are couple of reasons that I dare to think are also advantages.
First advantageObjects using events are able to live for the entire time the controller is alive.
A typical hook call is "live" only for the time of the call, unless you use $context or other globals, you are not able to share variables from one hook call to the other.
For the way events are built, instead, the "object" reacting to an event will be kept alive and will be able to react to other events as well, giving you the possibility to share code (usually variables) between different positions in the controller without having to pollute the global scope and without the risk another addon will conflict with your code. Unfortunately, at that moment I don't have any good example for this, so I'll resort to some pseudo code:
<?php
class Calendar_Post_Controller
{
protected $state = 0;
public function pre_load()
{
$this->state = 1;
}
public function post_load()
{
if ($this->state == 1)
{
// Do something
}
}
}
Assuming pre_load and post_load are two triggers, to do something like that with normal hooks you'd have to use $context:
<?php
function calendar_pre_load()
{
global $context;
$context['calendar_state'] = 1;
}
function calendar_post_load()
{
global $context;
if ($context['calendar_state'] == 1)
{
// Do something
}
}
Second advantageYou can "ask" for dependencies (in the form of variables) the controller.
Hooks are able to pass variables, but you have to take what they offer and you cannot do anything more.
With events, you will be able to ask the controller to pass you or not a wide range of variables:
variables normally passed by the trigger,
public or protected properties of the controller,
* global variables.
And you will be able to get them in the order you prefer.
Following the Calendar example, in Display_Controller, we need to react to the topcinfo event at:
$this->_events->trigger('topicinfo', array('topicinfo' => &$topicinfo));
this event by default passes only the $topicInfo variable, but in the Calendar_Display_Module we need more, we need also to know the topic, so writing the appropriated code we will be able to make the "Event_Manager" pass us the $topic variable writing something like:
array('blablabla', array('topicinfo', 'topic')),
the Event_Manage will take care of searching for what we want and will send it to our module.
A similar call will be associated to a method (a function of a class) with the following structure:
public function myfunction($topicInfo, $topic)
alternatively we could write:
array('blablabla', array('topic', 'topicinfo')),
that will be used in association to a function:
public function myfunction($topic, $topicInfo)
As you may have guessed, in order to request a dependency, you have to know its name.
This feature is of particular importance, because it basically allows you to extend the controller almost the same way if you were to edit its code, except for passing local variables not listed in those passed by the trigger, but I'm not even sure this is possible.
Next round I'll move explaining a bit the concept of "module" and finally I'll merge the two giving you the full (blurry) picture.
Thanks for all the documentation! I don't understand a word of it but what you guys do for us is really appreciated! :)
In the first part I talked about events, now it's time to give an idea of what a "module" is instead.
Modules... modules are just a name I made up to define a system that automagically loads stuff and should make easier and more consistent the code organization.
Let's start from the original idea: I wanted to build a system able to scan the file system, find files with a certain name and load the classes in there, attaching hooks "on-the-fly" without having to pass through the installation procedure.
After a first try I decided that scan the file system may or may not be the best way, for a start it may be slow, and, more importantly, it may not have a way to disable it, except delete the file.
So I went a step back and made the system a little less flexible, but also nicer (I think).
ElkArte (starting from version 1.1) is able to "discover" module in the admin panel.
Modules are discovered automatically when a class with the appropriate name, stored in an appropriate file is present either in sources/admin/ or sources/addons/{subdir}/
The naming pattern is:
Class: Manage{Nameofthemodule}_Controller
File: Manage{Nameofthemodule}Module.controller.php
For example, the calendar has
ManageCalendarModule_Controller in the file
ManageCalendarModule.controller.php.
With that combination ElkArte is able to find it in the admin panel and show a button...
drum roll ... in the Core Features page! :P
So, we create a file and a class and ElkArte is able to show us a button to enable the functionality directly in the admin panel. Isn't it cool? 8)
Well, it's not yet so easy, there is still something to do manually: add the button to the core features page. Yes, this is actually a design choice (for the moment), because modules are used to extend several controllers at different levels of complexity and as such it may not be necessary to add an entry to the core features page.
In order to add an entry to the Core Features page, the Mange*Module_Controller class needs a static method called
addCoreFeature accepting the $core_features array and adding the new entry to it, see the ManageCalendarModule_Controller as a reference:
https://github.com/elkarte/Elkarte/blob/8e042deefddd970a3e4ba120cb136845adbbb372/sources/admin/ManageCalendarModule.controller.php#L37
Then, in the setting_callback parameter of the Core Features button, we can proceed to enable (or disable) the modules when the button is pressed.
To enable a module we can use the enableModules function, that accepts two parameters: a string with the name of the module to enable, and an array with the name of the controllers the module will interact with. As an example:
enableModules('calendar', array('post', 'boardindex'));
to disable, same thing, just using the function disableModules.
Okay, for today that's all, it's late and the argument is quite tricky (even though explain it is more difficult than just reading the code), so I take a break and go bed.
Aaah... This is a nice guide. Parking in.
Can you explain object oriented php that are being used in ElkArte? I hope this function day will continue. :D
A quick search yields https://www.tutorialspoint.com/php/php_object_oriented.htm
Once you understand the fundamentals, OO code makes sense. I hear it's similar to Java, which I've never worked in...