Skip to content

Commit

Permalink
feature(js): Adds hooks to pass site and page-level data client-side
Browse files Browse the repository at this point in the history
Adds two hooks `"elgg.data"` that populate the object `elgg.data` available
in the `elgg` module.

Fixes Elgg#8997
  • Loading branch information
mrclay committed May 12, 2016
1 parent f81bf02 commit 262b668
Show file tree
Hide file tree
Showing 5 changed files with 144 additions and 46 deletions.
6 changes: 6 additions & 0 deletions docs/guides/hooks-list.rst
Expand Up @@ -117,6 +117,12 @@ System hooks

**add, river**

**elgg.data, site**
Filters cached configuration data to pass to the client. :ref:`More info <guides/javascript#config>`

**elgg.data, page**
Filters uncached, page-specific configuration data to pass to the client. :ref:`More info <guides/javascript#config>`

User hooks
==========

Expand Down
62 changes: 57 additions & 5 deletions docs/guides/javascript.rst
Expand Up @@ -77,9 +77,62 @@ the greeting:
$('body').append(hello);
});
.. _guides/javascript#config:

Passing settings to modules
---------------------------

The ``elgg.data`` plugin hooks
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The ``elgg`` module provides an object ``elgg.data`` which is populated from two server side hooks:

- **elgg.data, site**: This filters an associative array of site-specific data passed to the client and cached.
- **elgg.data, page**: This filters an associative array of uncached, page-specific data passed to the client.

Let's pass some data to a module:

.. code-block:: php
<?php
function myplugin_config_site($hook, $type, $value, $params) {
// this will be cached client-side
$value['myplugin']['api'] = elgg_get_site_url() . 'myplugin-api';
$value['myplugin']['key'] = 'none';
return $value;
}
function myplugin_config_page($hook, $type, $value, $params) {
$user = elgg_get_logged_in_user_entity();
if ($user) {
$value['myplugin']['key'] = $user->myplugin_api_key;
return $value;
}
}
elgg_register_plugin_hook_handler('elgg.data', 'site', 'myplugin_config_site');
elgg_register_plugin_hook_handler('elgg.data', 'page', 'myplugin_config_page');
.. code-block:: javascript
define(function(require) {
var elgg = require("elgg");
var api = elgg.data.myplugin.api;
var key = elgg.data.myplugin.key; // "none" or a user's key
// ...
});
.. note::

In ``elgg.data``, page data overrides site data. Also note ``json_encode()`` is used to copy
data client-side, so the data must be JSON-encodable.

Making a config module
^^^^^^^^^^^^^^^^^^^^^^

You can use a PHP-based module to pass values from the server. To make the module ``myplugin/settings``,
create the view file ``views/default/myplugin/settings.js.php`` (note the double extension
``.js.php``).
Expand All @@ -88,12 +141,11 @@ create the view file ``views/default/myplugin/settings.js.php`` (note the double
<?php
$settings = elgg_get_plugin_from_id('myplugin')->getAllSettings();
// this will be cached client-side
$settings = [
'foo' => elgg_extract('foo', $settings),
'bar' => elgg_extract('bar', $settings),
'api' => elgg_get_site_url() . 'myplugin-api',
'key' => null,
];
?>
define(<?php echo json_encode($settings); ?>);
Expand Down Expand Up @@ -669,7 +721,7 @@ The following example registers the ``handleFoo`` function for the ``foo, bar``
elgg.register_hook_handler('foo', 'bar', handleFoo);
return new Plugin();
});
});
The handler function
--------------------
Expand Down
74 changes: 74 additions & 0 deletions engine/lib/views.php
Expand Up @@ -1801,6 +1801,80 @@ function _elgg_manage_pagesetup($hook, $view, $value, $params) {
_elgg_services()->events->trigger('pagesetup', 'system');
}

/**
* Get the site data to be merged into "elgg" in elgg.js.
*
* @return array
* @access private
*/
function _elgg_get_js_site_data() {
$language = elgg_get_config('language');
if (!$language) {
$language = 'en';
}

return [
'elgg.data' => (object)elgg_trigger_plugin_hook('elgg.data', 'site', null, []),
'elgg.version' => elgg_get_version(),
'elgg.release' => elgg_get_version(true),
'elgg.config.wwwroot' => elgg_get_site_url(),

// refresh token 3 times during its lifetime (in microseconds 1000 * 1/3)
'elgg.security.interval' => (int)_elgg_services()->actions->getActionTokenTimeout() * 333,
'elgg.config.language' => $language,
];
}

/**
* Get the initial contents of "elgg" client side. Will be extended by elgg.js.
*
* @return array
* @access private
*/
function _elgg_get_js_page_data() {
$data = elgg_trigger_plugin_hook('elgg.data', 'page', null, []);
if (!is_array($data)) {
elgg_log('"elgg.data" plugin hook handlers must return an array. Returned ' . gettype($data) . '.', 'ERROR');
$data = [];
}

$elgg = array(
'config' => array(
'lastcache' => (int)elgg_get_config('lastcache'),
'viewtype' => elgg_get_viewtype(),
'simplecache_enabled' => (int)elgg_is_simplecache_enabled(),
),
'security' => array(
'token' => array(
'__elgg_ts' => $ts = time(),
'__elgg_token' => generate_action_token($ts),
),
),
'session' => array(
'user' => null,
),
'_data' => (object)$data,
);

if (elgg_get_config('elgg_load_sync_code')) {
$elgg['config']['load_sync_code'] = true;
}

$page_owner = elgg_get_page_owner_entity();
if ($page_owner instanceof ElggEntity) {
$elgg['page_owner'] = $page_owner->toObject();
}

$user = elgg_get_logged_in_user_entity();
if ($user instanceof ElggUser) {
$user_object = $user->toObject();
$user_object->admin = $user->isAdmin();
$elgg['session']['user'] = $user_object;
}

return $elgg;
}

return function(\Elgg\EventsService $events, \Elgg\HooksRegistrationService $hooks) {
$events->registerHandler('boot', 'system', 'elgg_views_boot');
$hooks->registerHandler('view_vars', 'all', '_elgg_manage_pagesetup', 1000);
Expand Down
16 changes: 6 additions & 10 deletions views/default/elgg.js.php
Expand Up @@ -68,19 +68,15 @@
echo "\n";
}

/**
* Set some values that are cacheable
*/
?>
//<script>
<?php foreach (_elgg_get_js_site_data() as $var => $value): ?>
<?= $var ?> = <?= json_encode($value) ?>;
<?php endforeach; ?>

elgg.version = '<?php echo elgg_get_version(); ?>';
elgg.release = '<?php echo elgg_get_version(true); ?>';
elgg.config.wwwroot = '<?php echo elgg_get_site_url(); ?>';

// refresh token 3 times during its lifetime (in microseconds 1000 * 1/3)
elgg.security.interval = <?php echo (int)_elgg_services()->actions->getActionTokenTimeout() * 333; ?>;
elgg.config.language = '<?php echo (empty($CONFIG->language) ? 'en' : $CONFIG->language); ?>';
// page data overrides site data
$.extend(elgg.data, elgg._data);
delete elgg._data;

// jQuery and UI must be loaded sync in 2.x but modules should depend on these AMD modules
define('jquery', function () {
Expand Down
32 changes: 1 addition & 31 deletions views/default/initialize_elgg.js.php
Expand Up @@ -2,39 +2,9 @@
/**
* Initialize Elgg's js lib with the uncacheable data
*/

$elgg = array(
'config' => array(
'lastcache' => (int)elgg_get_config('lastcache'),
'viewtype' => elgg_get_viewtype(),
'simplecache_enabled' => (int)elgg_is_simplecache_enabled(),
),
'security' => array(
'token' => array(
'__elgg_ts' => $ts = time(),
'__elgg_token' => generate_action_token($ts),
),
),
'session' => array(
'user' => null,
),
);

$page_owner = elgg_get_page_owner_entity();
if ($page_owner instanceof ElggEntity) {
$elgg['page_owner'] = $page_owner->toObject();
}

$user = elgg_get_logged_in_user_entity();
if ($user instanceof ElggUser) {
$user_object = $user->toObject();
$user_object->admin = $user->isAdmin();
$elgg['session']['user'] = $user_object;
}

?>

var elgg = <?php echo json_encode($elgg); ?>;
var elgg = <?php echo json_encode(_elgg_get_js_page_data()); ?>;
<?php
// note: elgg.session.user needs to be wrapped with elgg.ElggUser, but this class isn't
// defined yet. So this is delayed until after the classes are defined, in js/lib/session.js

0 comments on commit 262b668

Please sign in to comment.