Forms + Actions
###############
Create, update, or delete content.
Elgg forms submit to actions. Actions define the behavior for form submission.
This guide assumes basic familiarity with:
- :doc:`/admin/plugins`
- :doc:`views`
- :doc:`i18n`
.. contents:: Contents
:local:
:depth: 2
Registering actions
===================
Actions must be registered before use.
There are two ways to register actions:
Using ``elgg_register_action()``
.. code-block:: php
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``.
Use ``elgg-plugin.php``
.. code-block:: php
return [
'actions' => [
// defaults to using an action file in /actions/myplugin/action_a.php
'myplugin/action_a' => [
'access' => 'public',
],
// define custom action path
'myplugin/action_b' => [
'access' => 'admin',
'filename' => __DIR__ . '/actions/action.php'
],
// define a controller
'myplugin/action_c' => [
'controller' => \MyPlugin\Actions\ActionC::class,
],
],
];
.. warning::
A stumbling point for many new developers is the URL for actions. The URL always uses ``/action/`` (singular) and
never ``/actions/`` (plural). However, action script files are usually saved under the directory ``/actions/`` (plural)
and always have an extension. Use ``elgg_generate_action_url()`` to avoid confusion.
Registering actions using plugin config file
--------------------------------------------
You can also register actions via the :doc:`elgg-plugin` config file.
To do this you need to provide an action section in the config file.
The location of the action files are assumed to be in the plugin folder ``/actions``.
.. code-block:: php
[
'blog/save' => [], // all defaults
'blog/delete' => [ // all custom
'access' => 'admin',
'filename' => __DIR__ . 'actions/blog/remove.php',
],
],
];
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:
.. code-block:: php
elgg_register_action("example", $filepath, "public");
To restrict an action to only administrators, pass ``"admin"`` for the last parameter:
.. code-block:: php
elgg_register_action("example", $filepath, "admin");
To restrict an action to only logged out users, pass ``"logged_out"`` for the last parameter:
.. code-block:: php
elgg_register_action("example", $filepath, "logged_out");
Writing action files
--------------------
Use the ``get_input()`` function to get access to request parameters:
.. code-block:: php
$field = get_input('input_field_name', 'default_value');
You can then use the :doc:`database` api to load entities and perform actions on them accordingly.
To indicate a successful action, use ``elgg_ok_response()``. This function accepts data that you want to make available
to the client for XHR calls (this data will be ignored for non-XHR calls)
.. code-block:: php
$user = get_entity($guid);
// do something
$action_data = [
'entity' => $user,
'stats' => [
'friends_count' => $user->getEntitiesFromRelationship([
'type' => 'user',
'relationship' => 'friend',
'count' => true,
]);
],
];
return elgg_ok_response($action_data, 'Action was successful', 'url/to/forward/to');
To indicate an error, use ``elgg_error_response()``
.. code-block:: php
$user = elgg_get_logged_in_user_entity();
if (!$user) {
// show an error and forward the user to the referring page
// send 404 error code on AJAX calls
return elgg_error_response('User not found', REFERRER, ELGG_HTTP_NOT_FOUND);
}
if (!$user->canEdit()) {
// show an error and forward to user's profile
// send 403 error code on AJAX calls
return elgg_error_response('You are not allowed to perform this action', $user->getURL(), ELGG_HTTP_FORBIDDEN);
}
Customizing actions
-------------------
Before executing any action, Elgg triggers an event:
.. code-block:: php
$result = elgg_trigger_event_results('action:validate', $action, [], true);
Where ``$action`` is the action being called. If the event returns ``false`` then the action will not be executed. Don't return anything
if your validation passes.
Example: Captcha
^^^^^^^^^^^^^^^^
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 ``false`` if the captcha validation fails (which prevents the associated
action from executing).
This is done as follows:
.. code-block:: php
elgg_register_event_handler("action:validate", "register", "captcha_verify_action_event");
elgg_register_event_handler("action:validate", "user/requestnewpassword", "captcha_verify_action_event");
...
function captcha_verify_action_event(\Elgg\Event $event) {
$token = get_input('captcha_token');
$input = get_input('captcha_input');
if (($token) && (captcha_verify_captcha($input, $token))) {
return;
}
elgg_register_error_message(elgg_echo('captcha:captchafail'));
return false;
}
This lets a plugin extend an existing action without the need to replace the whole action. In the case of the captcha plugin it
allows the plugin to provide captcha support in a very loosely coupled way.
Actions available in core
=========================
``entity/delete``
-----------------
If your plugin does not implement any custom logic when deleting an entity, you can use bundled delete action
.. code-block:: php
$guid = 123;
// You can provide optional forward path as a URL query parameter
$forward_url = 'path/to/forward/to';
echo elgg_view('output/url', array(
'text' => elgg_echo('delete'),
'href' => elgg_generate_action_url('entity/delete', [
'guid' => $guid,
'forward_url' => $forward_url,
]),
'confirm' => true,
));
You can customize the success message keys for your entity type and subtype, using ``"entity:delete:$type:$subtype:success"``
and ``"entity:delete:$type:success"`` keys.
.. code-block:: php
// to add a custom message when a blog post or file is deleted
// add the translations keys in your language files
return [
'entity:delete:object:blog:success' => 'Blog post has been deleted,
'entity:delete:object:file:success' => 'File titled %s has been deleted',
];
Forms
=====
To output a form, use the elgg_view_form function like so:
.. code-block:: php
echo elgg_view_form('example');
Doing this generates something like the following markup:
.. code-block:: html
Elgg does some things automatically for you when you generate forms this way:
1. It sets the action to the appropriate URL based on the name of the action you pass to it
2. It adds some anti-csrf tokens (``__elgg_ts`` and ``__elgg_token``) to help keep your actions secure
3. It automatically looks for the body of the form in the ``forms/example`` view.
Put the content of your form in your plugin’s ``forms/example`` view:
.. code-block:: php
// /mod/example/views/default/forms/example.php
echo elgg_view('input/text', array('name' => 'example'));
// defer form footer rendering
// this will allow other plugins to extend forms/example view
elgg_set_form_footer(elgg_view('input/submit'));
Now when you call ``elgg_view_form('example')``, Elgg will produce:
.. code-block:: html
Inputs
------
To render a form input, use one of the bundled input views, which cover all standard
HTML input elements. See individual view files for a list of accepted parameters.
.. code-block:: php
echo elgg_view('input/select', array(
'required' => true,
'name' => 'status',
'options_values' => [
'draft' => elgg_echo('status:draft'),
'published' => elgg_echo('status:published'),
],
// most input views will render additional parameters passed to the view
// as tag attributes
'data-rel' => 'blog',
));
The above example will render a dropdown select input:
.. code-block:: html
To ensure consistency in field markup, use ``elgg_view_field()``, which accepts
all the parameters of the input being rendered, as well as ``#label`` and ``#help``
parameters (both of which are optional and accept HTML or text).
.. code-block:: php
echo elgg_view_field([
'#type' => 'select',
'#label' => elgg_echo('blog:status:label'),
'#help' => elgg_view_icon('help') . elgg_echo('blog:status:help'),
'required' => true,
'name' => 'status',
'options_values' => [
'draft' => elgg_echo('status:draft'),
'published' => elgg_echo('status:published'),
],
'data-rel' => 'blog',
]);
The above will generate the following markup:
.. code-block:: html
This indicates whether or not the blog is visible in the feed
Input types
-----------
A list of bundled input types/views:
* ``input/text`` - renders a text input ````
* ``input/plaintext`` - renders a textarea ````
* ``input/longtext`` - renders a WYSIWYG text input
* ``input/url`` - renders a url input ````
* ``input/email`` - renders an email input ````
* ``input/checkbox`` - renders a single checkbox ````
* ``input/checkboxes`` - renders a set of checkboxes with the same name
* ``input/radio`` - renders one or more radio buttons ````
* ``input/submit`` - renders a submit button ``