All notes



cp vendor/elgg/elgg/elgg-config/settings.example.php elgg-config/settings.php
# wcfNote: set the db-prefix to empty ''.

#---------- wcfMethod

# If install.php says can't connect to DB: Create an empty database e.g. "rp".

# Create a tmp data directory
mkdir /tmp/myproj
chmod a+w /tmp/myproj

#---------- Method: through composer

cd /var/www/myProj
composer self-update
composer global require "fxp/composer-asset-plugin:~1.1.1"
composer create-project elgg/starter-project:dev-master .
composer install


# Enable .htaccess processing
AllowOverride All
# Restart Apache

nginx php-fpm

Docker elgg. linuxConfig: elgg.

The Elgg application runs on Debian GNU/Linux system featuring Apache web server, MariaDB (MySQL), database and PHP5.

Configured MySQL users:passwords:
root:"empty password"

Configured MySQL databases:

Full path to elgg data directory:





The latest 3.x guide.

Views views.

Using views

Assuming a view is located at "/views/default/hello/world.php" (may be a whole HTML page), we could output it like so:

echo elgg_view('hello/world');

Views as templates, passing parameter

  <h1>Hello, <?= $vars['name']; ?>!</h1>

// In this case, we can pass an arbitrary name parameter to the view like so:
echo elgg_view('hello/world', ['name' => 'World']);

// which would produce the following output:
  <h1>Hello, World!</h1>

Views as cacheable assets

Views can contain JS, CSS, or even images. Asset views must meet certain requirements:

For a file "/views/default/mystyles.css", to get a URL to this file, you would use:

// Returns ""
// Elgg automatically adds the magic numbers you see there (e.g. 289124335 here) for cache-busting and sets long-term expires headers on the returned file.

//---------- Load CSS
// In your plugin’s init function, register the css file:
elgg_register_css('mystyles', elgg_get_simplecache_url('mystyles.css'));
// Then on the page you want to load the css, call:

Third-party assets

Instead of manually copy/pasting the assets into the right location in /views/*, you can use a views.php file in your plugin’s directory to map the assets into the views system.

A views file must return a 2 dimensional array. The first level maps a viewtype to a list of view mappings. The secondary lists map view names to file paths, either absolute or relative to the Elgg root directory.

//---------- Point to assets checked in your repo:
<?php // mod/example/views.php
return [
    // viewtype
    'default' => [
        // view => /path/from/filesystem/root
        'js/jquery-ui.js' => __DIR__ . '/bower_components/jquery-ui/jquery-ui.min.js',

//---------- Point to assets installed with fxp/composer-asset-plugin
<?php // mod/example/views.php
return [
    // viewtype
    'default' => [
        // view => path/from/install/root
        'js/jquery-ui.js' => 'vendor/bower-asset/jquery-ui/jquery-ui.min.js',

//---------- Specifying additional views directories
<?php // mod/file/views.php
return [
    'default' => [
        'file/icon/' => __DIR__ . '/graphics/icons',
// With the above, files found within the icons folder will be interpreted as views. E.g. the view file/icon/general.gif will be created and mapped to mod/file/graphics/icons/general.gif.


Why /views/default/hello/world.php instead of just /views/hello/world.php?

The "default" viewtype is assumed to be HTML and other static assets necessary to render a responsive web page in a desktop or mobile browser, but it could also be: RSS, ATOM, JSON, etc.

You can force Elgg to use a particular viewtype to render the page by setting the view input variable like so:

You could also write a plugin to set this automatically using the elgg_set_viewtype() function:


Altering views via plugins

Overriding views

Views in plugin directories always override views in the core directory; however, when plugins override the views of other plugins, later plugins take precedent.

Note: the plugin orders are set in the admin-plugins page.

Overriding views in the core and bundled plugins has a cost: Upgrades may bring changes in views, and if you have overridden them, you will not get those changes.

You may instead want to alter the input or the output of the view via plugin hooks.

Extending views

There are cases you just want to prepend or append some more content to it. In Elgg, this is called extending a view.

// appends 'hello/greeting' to every occurrence of 'hello/world'
elgg_extend_view('hello/world', 'hello/greeting');

// prepends 'hello/greeting' to every occurrence of 'hello/world', by passing a value to the 3rd parameter that is less than 500:
elgg_extend_view('hello/world', 'hello/greeting', 450);

All view extensions should be registered in your plugin’s "init,system" event handler in start.php.

Altering view input

Since 1.11, before each view rendering the $vars array is filtered by the plugin hook ["view_vars", \$view_name]. Each registered handler function is passed these arguments:

$hook - the string "view_vars"
$view_name - the view name being rendered (the first argument passed to elgg_view())
$returnvalue - the modified $vars array
$params - an array containing:
  vars - the original $vars array, unaltered
  view - the view name
  viewtype - The viewtype being rendered

//---------- Alter the default pagination limit for the comments view:

// Format: elgg_register_plugin_hook_handler("view_vars", $view_name, callback)
elgg_register_plugin_hook_handler('view_vars', 'page/elements/comments', 'myplugin_alter_comments_limit');

function myplugin_alter_comments_limit($hook, $type, $vars, $params) {
    // only 10 comments per page
    $vars['limit'] = elgg_extract('limit', $vars, 10);
    return $vars;

Altering view output

Sometimes it is preferable to alter the output of a view instead of overriding it.

The output of each view is run through the plugin hook ["view", $view_name] before being returned by elgg_view(). Each registered handler function is passed these arguments:

$hook - the string "view"
$view_name - the view name being rendered (the first argument passed to elgg_view())
$result - the modified output of the view
$params - an array containing:
  viewtype - The viewtype being rendered

To alter the view output, the handler just needs to alter \$returnvalue and return a new string.

//---------- Eliminate breadcrumbs that don’t have at least one link

elgg_register_plugin_hook_handler('view', 'navigation/breadcrumbs', 'myplugin_alter_breadcrumb');

function myplugin_alter_breadcrumb($hook, $type, $returnvalue, $params) {
    // we only want to alter when viewtype is "default"
    if ($params['viewtype'] !== 'default') {
        return $returnvalue;

    // output nothing if the content doesn't have a single link
    if (false === strpos($returnvalue, '<a ')) {
        return '';

    // returning nothing means "don't alter the returnvalue"

Displaying entities

The following code will automatically display the entity in \$entity:

echo elgg_view_entity($entity);

elgg_view_entity will automatically look for a view called type/subtype; if there’s no subtype, it will look for type/type. Failing that, before it gives up completely it tries type/default.

For example, the view to display a blog post might be object/blog. The view to display a user is user/default.

RSS feeds in Elgg generally work by outputting the object/default view in the ‘rss’ viewtype.

Listing entities

//---------- To automatically display a list of blog posts

echo elgg_list_entities([
    'type' => 'object',
    'subtype' => 'blog',

This function checks to see if there are any entities; if there are, it first displays the navigation/pagination view in order to display a way to move from page to page. It then repeatedly calls elgg_view_entity on each entity before returning the result.

Note that elgg_list_entities allows the URL to set its "limit" and "offset" options.


The Simplecache is a mechanism designed to alleviate the need for certain views to be regenerated dynamically. Instead, they are generated once, saved as a static file, and served in a way that entirely bypasses the Elgg engine.

If Simplecache is turned off (which can be done from the administration panel / advanced settings), these views will be served as normal, with the exception of site CSS.

Forms, Actions

Registering actions

//---------- Registering

elgg_register_action("example", __DIR__ . "/actions/example.php");
// The mod/example/actions/example.php script will now be run whenever a form is submitted to http://localhost/elgg/action/example.
// Note: the URL uses "action" while the directory uses "actions".

//---------- Permissions

// By default, actions are only available to logged in users.
// To make an action available to logged out users, pass "public" as the third parameter:
elgg_register_action("example", $filepath, "public");
// To restrict an action to only administrators, pass "admin" for the last parameter:
elgg_register_action("example", $filepath, "admin");

Action files

// Get access to request parameters:
$field = get_input('input_field_name', 'default_value');

// To redirect the page once you’ve completed your actions, use the forward function:
// For example, to forward to the user’s profile:
$user = elgg_get_logged_in_user_entity();
// URLs can be relative to the Elgg root:
// Redirect to the referring page by using the REFERRER constant:
forward(REFERER); // equivalent

// Give feedback to the user about the status of the action
if ($success) {
} else {

Customizing actions

Before executing any action, Elgg triggers a hook:

$result = elgg_trigger_plugin_hook('action', $action, null, true);
// Where $action is the action being called. If the hook returns false then the action will not be executed.

The captcha module uses this to intercept the register and user/requestnewpassword actions and redirect them to a function which checks the captcha code. This check returns true if valid or false if not (which prevents the associated action from executing).

elgg_register_plugin_hook_handler("action", "register", "captcha_verify_action_hook");
elgg_register_plugin_hook_handler("action", "user/requestnewpassword", "captcha_verify_action_hook");

function captcha_verify_action_hook($hook, $entity_type, $returnvalue, $params) {
  $token = get_input('captcha_token');
  $input = get_input('captcha_input');

  if (($token) && (captcha_verify_captcha($input, $token))) {
    return true;


  return false;

Generating forms

To output a form, use the elgg_view_form function like so: echo elgg_view_form('example');.

Elgg does some things automatically for you when you generate forms this way:

Put the content of your form in your plugin’s forms/example view:

// /mod/example/views/default/forms/example.php
echo elgg_view('input/text', array('name' => 'example'));
echo elgg_view('input/submit');

It will produce:

<form action="http://localhost/elgg/action/example">
    <input type="hidden" name="__elgg_ts" value="...">
    <input type="hidden" name="__elgg_token" value="...">

    <input type="text" class="elgg-input-text" name="example">
    <input type="submit" class="elgg-button elgg-button-submit" value="Submit">

Files and Images

// Use the input/file view in your form’s content view.

// /mod/example/views/default/forms/example.php
echo elgg_view(‘input/file’, array(‘name’ => ‘icon’));

// Set the enctype of the form to multipart/form-data:
echo elgg_view_form(‘example’, array(
  ‘enctype’ => ‘multipart/form-data’

// In your action file, use the $_FILES global to access the uploaded file:
$icon = $_FILES[‘icon’]

Sticky forms

Sticky forms are forms that retain user input if saving fails. They are “sticky” because the user’s data “sticks” in the form after submitting, though it was never saved to the database.

The basic flow of using sticky forms is:
1. Call elgg_make_sticky_form($name) at the top of actions for forms you want to be sticky.
2. Use elgg_is_sticky_form($name) and elgg_get_sticky_values($name) to get sticky values when rendering a form view.
3. Call elgg_clear_sticky_form($name) after the action has completed successfully or after data has been loaded by elgg_get_sticky_values($name).

Databases database.

Plugins are discouraged from interacting directly with the database.


Every entity in the system inherits the ElggEntity class. This class controls access permissions, ownership.

You can extend entities with extra information in two ways:


Entities have a owner_guid GUID property, which defines its owner. Typically this refers to the GUID of a user, although sites and users themselves often have no owner (a value of 0).


In order to easily search content by group or by user, content is generally set to be “contained” by either the user who posted it, or the group to which the user posted. This means the new object’s container_guid property will be set to the GUID of the current ElggUser or the target ElggGroup.


Type	PHP class	Represents
object	ElggObject	Most user-created content, like blog posts, uploads, and bookmarks.
group	ElggGroup	An organized group of users with its own profile page
user	ElggUser	A system user
site	ElggSite	The site served by the Elgg installation


The plugins use it to further specialize the entity.

Subtypes are most commonly given to instances of ElggObject to denote the kind of content created. E.g. the blog plugin creates objects with subtype "blog".


//---------- Add annotations

public function annotate(
    $name,           // The name of the annotation type (eg 'comment')
    $value,          // The value of the annotation
    $access_id = 0,  // The access level of the annotation
    $owner_id = 0,   // The annotation owner, defaults to current user
    $vartype = ""    // 'text' or 'integer'
// For example, to leave a rating on an entity, you might call:
$entity->annotate('rating', $rating_value, $entity->access_id);

//---------- Read annotations

$annotations = $entity->getAnnotations(
    $name,    // The type of annotation
    $limit,   // The number to return
    $offset,  // Any indexing offset
    $order,   // 'asc' or 'desc' (default 'asc')

// If your annotation type largely deals with integer values, a couple of useful mathematical functions are provided:
$averagevalue = $entity->getAnnotationsAvg($name);  // Get the average value
$total = $entity->getAnnotationsSum($name);         // Get the total value
$minvalue = $entity->getAnnotationsMin($name);      // Get the minimum value
$maxvalue = $entity->getAnnotationsMax($name);      // Get the maximum value


Under the hood, metadata is stored as an instance of the ElggMetadata class.

//---------- Add metadata

// To add a piece of metadata to an entity, just call:
$entity->metadata_name = $metadata_value;

// For example, to add a date of birth to a user:
$user->dob = $dob_timestamp;
// Or to add a couple of tags to an object:
$object->tags = array('tag one', 'tag two', 'tag three');

//---------- Read metadata

$tags_value = $object->tags;

// If you stored only one value, you will get a string or integer back. Storing an array with only one value will return a string back to you. E.g.
$object->tags = array('tag');
$tags = $object->tags;
// $tags will be the string "tag", NOT array('tag')
// To always get an array back, simply cast to an array;
$tags = (array)$object->tags;


The class ElggRelationship models a directed relationship between two entities, making the statement: “{subject} is a {noun} of {target}.”

The type of relationship may alternately be a verb, making the statement: “{subject} {verb} {target}.” E.g. User A “likes” blog post B.

Each relationship has direction.

A relationship does not imply reciprocity. A follows B does not imply that B follows A.

//---------- Create relationship.

// To establish that “$user is a fan of $artist” (user is the subject, artist is the target):
// option 1
$success = add_entity_relationship($user->guid, 'fan', $artist->guid);
// option 2
$success = $user->addRelationship($artist->guid, 'fan');

//---------- Verifying a relationship

// E.g. to verify that “$user is a fan of $artist”:
if (check_entity_relationship($user->guid, 'fan', $artist->guid)) {
    // relationship exists
// check_entity_relationship() returns an ElggRelationship object.

//---------- Deleting a relationship

// E.g. to be able to assert that “$user is no longer a fan of $artist”:
$was_removed = remove_entity_relationship($user->guid, 'fan', $artist->guid);

//---------- Search for a relationship

// retrieving users who joined your site in January 2014.
$entities = elgg_get_entities_from_relationship(array(
    'relationship' => 'member_of_site',
    'relationship_guid' => elgg_get_site_entity()->guid,
    'inverse_relationship' => true,
    'relationship_created_time_lower' => 1388534400, // January 1st 2014
    'relationship_created_time_upper' => 1391212800, // February 1st 2014

Access control

Pre-defined access controls

ACCESS_PRIVATE (value: 0) Private.
ACCESS_LOGGED_IN (value: 1) Logged in users.
ACCESS_PUBLIC (value: 2) Public data.
ACCESS_FRIENDS (value: -2) Owner and his/her friends.


The database contains a number of primary tables and secondary tables. Its schema table is stored in /engine/schema/mysql.sql.

Routing routing.

Elgg has two mechanisms to respond to HTTP requests that don’t already go through the Actions and Simplecache systems. wcfNote: what does this mean?

Identifier and Segments

If the site URL is, the URL produces:

Identifier: 'blog'.
Segments: ['owner', 'jane'].
the query string parameters are available via get_input().

Page Handling

// in your 'init', 'system' handler
elgg_register_route('my_plugin:section', [
        'path' => '/my_plugin/section/{guid}/{subsection?}',
        'resource' => 'my_plugin/section',
        'requirements' => [
                'guid' => '\d+',
                'subsection' => '\w+',

// in my_plugin/views/default/resources/my_plugin/section.php
$guid = elgg_extract('guid', $vars);
$subsection = elgg_extract('subsection', $vars);

// In the example above, we have registered a new route that is accessible via http://yoursite/my_plugin/section/<guid>/<subsection>. Whenever that route is accessed with a required guid segment and an optional subsection segment, the router will render the specified my_plugin/section resource view and pass the parameters extracted from the URL to your resource view with $vars.

//---------- Route names can then be used to generate a URL:

$url = elgg_generate_url('my_plugin:section', [
        'guid' => $entity->guid,
        'subsection' => 'assets',

Naming conventions

The route names are unique across all plugins and core.

The following conventions are used in core and recommended for plugins:

Maps to the entity profile page, e.g. view:user:user or view:object:blog.
The path must contain a guid, or username for users.

Maps to the form to edit the entity, e.g. edit:user:user or edit:object:blog.
The path must contain a guid, or username for users.
If you need to add subresources, use suffixes, e.g. edit:object:blog:images, keeping at least one subresource as a default without suffix.

Maps to the form to add a new entity of a given type, e.g. add:object:blog.
The path, as a rule, contains container_guid parameter.

Maps to listing pages. Common route names used in core are, as follows:
  collection:object:blog:all: list all blogs
  collection:object:blog:owner: list blogs owned by a user with a given username
  collection:object:blog:friends: list blogs owned by friends of the logged in user (or user with a given username)
  collection:object:blog:group: list blogs in a group

Maps to the default page for a resource, e.g. the path /blog.
Elgg happens to use the “all” collection for these routes.
default:object:blog: handle the generic path /blog.

<entity_subtype> can be omitted from route names to register global routes applicable to all entities of a given type.

Route controllers

In certain cases, using resource views is not appropriate. In these cases you can use a controller - any callable that receives an instance of \Elgg\Request.


The format is /languages/{language-code}.php where {language-code} is the ISO 639-1 short code for the language. For example:

zh_hans: simplified chinese
zh_hant: traditional chinese

wcfNote: ISO 639-1 uses "zh-Hans", but in elgg it becomes "zh_hans". Note the '-' becomes "_", and char cases.

<?php // mod/example/languages/en.php
return [
        'example:text' => 'Some example text',


// To override an existing translation, include it in your plugin’s language file, and make sure your plugin is ordered later on the Admin > Plugins page:

<?php // mod/better_example/languages/en.php
return [
        'example:text' => 'Some better text!',

Error Handling

Under the hood, Elgg uses Monolog for logging errors to the server’s error log (and stdout for CLI commands).

// Add a new handler to notify a given email about a critical error
        new \Monolog\Handler\NativeMailerHandler(
                'Critical error',



// Checks for $array[$key] and returns its value if it exists, else returns $default.
function elgg_extract($key, $array, $default = null, $strict = true)

function elgg_register_css($name, $url, $priority = null);

function elgg_load_css($name);
// This must be called before elgg_view_page(). It can be called before the CSS file is registered. If you do not want a CSS file loaded, unregister it.

// Forward to $location.
// Sends a 'Location: $location' header and exits.  If headers have already been sent, throws an exception.
function forward($location = "", $reason = 'system');


// Display a system message on next page load.
function system_message($message);
// Display an error on next page load.
function register_error($error);


elgg_register_js (
	$location = 'head',
	$priority = null 
// Parameters
//     string	$name	An identifier for the JavaScript library
//     string	$url	URL of the JavaScript file
//     string	$location	Page location: head or footer. (default: head)
//     int	$priority	Priority of the JS file (lower numbers load earlier)

elgg_unregister_js ($name)

elgg_define_js ($name, $config)
// Defines a JS lib as an AMD module.
// Calling this function is not needed if your JS are in views named like module/name.js Instead, simply call elgg_require_js("module/name").
// Example:
elgg_define_js('jquery.ui.autocomplete.html', [
    'deps' => ['jquery-ui'],

elgg_require_js	($name)
// Request that Elgg load an AMD module onto the page.
elgg_unrequire_js ($name)
// Cancel a request to load an AMD module onto the page.

elgg_load_js ($name)
// Load a JavaScript resource on this page.
// This must be called before elgg_view_page(). It can be called before the script is registered. If you do not want a script loaded, unregister it.
// Example:
elgg_register_js('jquery', elgg_get_simplecache_url('jquery.js'), 'head');

elgg_get_loaded_js ($location= 'head')
// Get the JavaScript URLs that are loaded.

How to use third party js

//---------- Method 1

// Register the script for synchronous loading in your start.php
elgg_register_js('external_script_name', $external_url);

// Load the script where you need it

//---------- Method 2

// Define the module
elgg_define_js('moment', [
   'src' => $cdn_url,
   'exports' => 'moment',

// In your AMD module
define(function(require) {
   var moment = require('moment');
   var $ = require('jquery');


function get_entity($guid);

// This function is very complex, and is SQL related. See its comment for detail.
function elgg_get_entities(array $options = []);

//----- Relationship

$entities = elgg_get_entities(array(
    'relationship' => 'member',
    'relationship_guid' => $group->guid,
    'inverse_relationship' => true,

    'relationship_created_time_lower' => 1388534400, // January 1st 2014
    'relationship_created_time_upper' => 1391212800, // February 1st 2014

// Get all entities that user with guid 25 has friended or been friended by
$options['relationship_pairs'] = [
      'relationship' => 'friend',
      'relationship_guid' => 25,
      'inverse_relationship' => true,
      'relationship' => 'friend',
      'relationship_guid' => 25,
      'inverse_relationship' => false,

function elgg_list_entities(array $options = [], $getter = 'elgg_get_entities', $viewer = 'elgg_view_entity_list')


get_input, elgg_get_sticky_values

//----- get_input($variable, $default = null, $filter_result = true).
// Get some input from variables passed submitted through GET or POST.
$username = get_input('u');
$email = get_input('e');
$name = get_input('n');

//----- function elgg_get_title_input($variable = 'title', $default = '')
// Get an HTML-escaped title from input. E.g. "How to use &lt;b&gt; tags".

//----- elgg_make_sticky_form($form_name)
//   Save form submission data (all GET and POST vars) into a session cache.
//----- elgg_clear_sticky_form($form_name)

//----- elgg_get_sticky_value($form_name, $variable = '', $default = null, $filter_result = true).
//----- elgg_get_sticky_values($form_name, $filter_result = true). It returns an array.


// Format an HTML element
function elgg_format_element($tag_name, array $attributes = [], $text = '', array $options = []);

// Converts shorthand URLs to absolute URLs.
// elgg_normalize_url('');                   // ''
// elgg_normalize_url('dashboard');          // ''
// elgg_normalize_url(''); // no change
function elgg_normalize_url($url);


function elgg_echo($message_key, array $args = [], $language = "");
// $message_key The short message code
// $args        An array of arguments to pass through vsprintf().
// $language    Optionally, the standard language code
//              (defaults to site/user default, then English)


function elgg_ok_response($content = '', $message = '', $forward_url = null, $status_code = ELGG_HTTP_OK);

function elgg_error_response($error = '', $forward_url = REFERRER, $status_code = ELGG_HTTP_OK);

function elgg_generate_url($name, array $parameters = []);

function elgg_register_route($name, array $params = []);

//---------- Ajax

// Require that the current request be an XHR. If not, execution of the current function will end and a 400 response page will be sent.
function elgg_ajax_gatekeeper();


function elgg_get_logged_in_user_guid();
function elgg_get_logged_in_user_entity();

function elgg_is_logged_in();
function elgg_is_admin_logged_in();

function elgg_is_admin_user($user_guid);


// returning false if the username already exists.
register_user($username, $password, $name, $email, $allow_multiple_emails = false, $subtype = null)

//---------- Get users
function get_user($guid);
function get_user_by_username($username);
function get_user_by_code($hash); // Get user by persistent login password.
function get_user_by_email($email);

function find_active_users(array $options = []);


// Register a view to be available for ajax calls
// @warning Only views that begin with 'js/' and 'css/' have their content type set to 'text/javascript' and 'text/css'. Other views are served as 'text/html'.
function elgg_register_ajax_view($view);

function elgg_view($view, $vars = [], $viewtype = '');
// Return a parsed view.

function elgg_view_form($action, $form_vars = [], $body_vars = []);
// return string. The complete form.
// $form_vars: $vars environment passed to the "input/form" view.
// $body_vars: $vars environment passed to the "forms/$action" view.
//-- Example from blog:
$content = elgg_view_form('blog/save', $vars, blog_prepare_form_vars());
$layout = elgg_view_layout('default', [
	'title' => $title,
	'content' => $content,
	'filter_id' => 'blog/edit',
echo elgg_view_page($title, $layout);

function elgg_view_layout($layout_name, $vars = []);
// Plugins should use one of the core layouts:
//  *  - default     Primary template with one, two or no sidebars
//  *  - admin       Admin page template
//  *  - error       Error page template
//  *  - widgets     Widgets canvas
// string $layout_name Layout name. Corresponds to a view in "page/layouts/<layout_name>".
//  For a full list of parameters supported by each of these layouts see corresponding layout views.

function elgg_view_page($title, $body, $page_shell = 'default', $vars = []);
// Assembles and outputs a full page.

function elgg_view_field(array $params = []);

function elgg_view_resource($name, array $vars = []);
// Use this in your page handler to hand off page rendering to a view in "resources/".
// $name The view name without the leading "resources/"



'ajax' => [
	'path' => '/ajax/{segments}',
	'handler' => '_elgg_ajax_page_handler',

// From engine/lib/elgglib.php:
function _elgg_ajax_page_handler($segments);
// Serve individual views for Ajax.
// /ajax/view/<view_name>?<key/value params>
// /ajax/form/<action_name>?<key/value params>


TODO: how to set english and chinese translations.




function elgg_register_plugin_hook_handler($hook, $type, $callback, $priority = 500) {
	return _elgg_services()->hooks->registerHandler($hook, $type, $callback, $priority);

elgg_register_plugin_hook_handler('index', 'system', 'facebook_theme_index_handler');




function elgg_register_page_handler($identifier, $function) {
	return _elgg_services()->router->registerPageHandler($identifier, $function);

// Registers a page handler for a particular identifier
// For example, you can register a function called 'blog_page_handler' for the identifier 'blog'
// For all URLs  http://yoururl/blog/*, the blog_page_handler() function will be called.
// The part of the URL marked with * above will be exploded on '/' characters and passed as an
// array to that function.
// For example, the URL http://yoururl/blog/username/friends/ would result in the call:
// blog_page_handler(array('username','friends'), blog);




var Ajax = require('elgg/Ajax');
var ajax = new Ajax();

ajax.path('myplugin_time').done(function (output, statusText, jqXHR) {
    if (jqXHR.AjaxData.status == -1) {