Формы и действия
Действия — это основной способ взаимодействия пользователей с сайтом на Elgg. Формы Elgg отправляются в действия. Действия определяют поведение при отправке формы.
Обзор
Действие в Elgg — это код, который выполняется для внесения изменений в базу данных, когда пользователь что-то делает. Например, вход в систему, публикация комментария и создание записи в блоге — это действия. Скрипт действия обрабатывает ввод, вносит соответствующие изменения в базу данных и предоставляет пользователю обратную связь о действии.
Обработчик действия
Действия регистрируются в процессе загрузки путём вызова elgg_register_action(). Все URL действий начинаются с action/ и обрабатываются фронтенд-контроллером Elgg через сервис маршрутизации. Стандартный промежуточный слой действия выполняет проверки безопасности CSRF.
Это руководство предполагает базовое знакомство с:
Регистрация действий
Действия должны быть зарегистрированы перед использованием.
Существует два способа регистрации действий:
Использование elgg_register_action()
elgg_register_action("example", __DIR__ . "/actions/example.php");
Скрипт mod/example/actions/example.php теперь будет выполняться каждый раз, когда форма отправляется на http://localhost/elgg/action/example.
Использование elgg-plugin.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,
],
],
];
Предупреждение
Камнем преткновения для многих новых разработчиков является URL для действий. URL всегда использует /action/ (в единственном числе) и никогда /actions/ (во множественном числе). Однако файлы скриптов действий обычно сохраняются в директории /actions/ (во множественном числе) и всегда имеют расширение. Используйте elgg_generate_action_url(), чтобы избежать путаницы.
Регистрация действий с использованием файла конфигурации плагина
Вы также можете зарегистрировать действия через файл конфигурации elgg-plugin. Для этого вам нужно предоставить секцию action в файле конфигурации. Предполагается, что файлы действий находятся в папке плагина /actions.
<?php
return [
'actions' => [
'blog/save' => [], // all defaults
'blog/delete' => [ // all custom
'access' => 'admin',
'filename' => __DIR__ . 'actions/blog/remove.php',
],
],
];
Разрешения
По умолчанию действия доступны только авторизованным пользователям.
Чтобы сделать действие доступным для неавторизованных пользователей, передайте "public" в качестве третьего параметра:
elgg_register_action("example", $filepath, "public");
Чтобы ограничить действие только администраторами, передайте "admin" в качестве последнего параметра:
elgg_register_action("example", $filepath, "admin");
Чтобы ограничить действие только неавторизованными пользователями, передайте "logged_out" в качестве последнего параметра:
elgg_register_action("example", $filepath, "logged_out");
Написание файлов действий
Используйте функцию get_input() для получения доступа к параметрам запроса:
$field = get_input('input_field_name', 'default_value');
Затем вы можете использовать API База данных для загрузки сущностей и выполнения над ними соответствующих действий.
Чтобы указать на успешное действие, используйте elgg_ok_response(). Эта функция принимает данные, которые вы хотите сделать доступными для клиента при вызовах XHR (эти данные будут проигнорированы при не-XHR вызовах)
$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');
Чтобы указать на ошибку, используйте elgg_error_response()
$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);
}
Написание контроллеров действий
В качестве альтернативы файлам действий также можно написать код действия в классе контроллера.
Вы можете расширить общий класс действия \Elgg\Controllers\GenericAction, чтобы он вызывался с набором функций в следующем порядке:
sanitize()— очистка вашего вводаvalidate()— проверка ввода или проверка разрешенийexecuteBefore()— подготовка перед основным выполнениемexecute()— основное выполнение действияexecuteAfter()— выполняется после основного выполненияsuccess()— если ничего не пошло не так, обработка успешного ответаerror()— если один из предыдущих шагов выбрасывает исключение, оно будет обработано шагом error путём отображения ошибки как системного сообщения и перенаправления обратно наREFERER
Для действий сохранения/редактирования сущностей существует дополнительный вспомогательный контроллер \Elgg\Controllers\EntityEditAction. Этот контроллер выполнит большинство общих проверок на основе конфигурации полей сущности. Он также создаст элемент ленты при создании новой сущности. При использовании/расширении этого контроллера обязательно настройте тип/подтип сущности при регистрации действия.
// elgg-plugin.php
return [
'actions' => [
'my_entity/save' => [
'controller' => \Elgg\Controllers\EntityEditAction::class,
'options' => [
'entity_type' => 'object',
'entity_subtype' => 'my_entity',
],
],
],
];
Настройка действий
Перед выполнением любого действия Elgg запускает событие:
$result = elgg_trigger_event_results('action:validate', $action, [], true);
Где $action — это вызываемое действие. Если событие возвращает false, то действие не будет выполнено. Не возвращайте ничего, если ваша проверка прошла успешно.
Пример: Captcha
Модуль captcha использует это для перехвата действий register и user/requestnewpassword и перенаправления их в функцию, которая проверяет код captcha. Эта проверка возвращает false, если проверка captcha не прошла (что предотвращает выполнение связанного действия).
Это делается следующим образом:
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;
}
Это позволяет плагину расширять существующее действие без необходимости заменять всё действие. В случае плагина captcha это позволяет плагину предоставлять поддержку captcha очень слабо связанным способом.
Действия, доступные в ядре
entity/delete
Если ваш плагин не реализует никакой пользовательской логики при удалении сущности, вы можете использовать встроенное действие удаления
$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,
));
Вы можете настроить ключи сообщений об успехе для вашего типа и подтипа сущности, используя ключи "entity:delete:$type:$subtype:success" и "entity:delete:$type:success".
// 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',
];
Поля
Формы и действия для хранения (сохранение/редактирование) сущностей используют различные поля для предоставления ввода пользователям, а также для проверки ввода. Формы и действия должны поддерживать эти конфигурации полей в актуальном состоянии. Для помощи в этом доступен общий FieldsService для доступа к настроенным полям для определённого типа/подтипа сущности.
Эти поля можно запросить с помощью одной из следующих функций:
$entity->getFields()— получает конфигурацию полей для сущностиelgg()->fields->get('entity_type', 'entity_subtype')— получает конфигурацию полей для заданного типа/подтипа
Обе функции вернут массив конфигураций полей, который можно напрямую передать в elgg_view_field($field_config).
Разработчики могут настроить набор полей по умолчанию для своих сущностей в соответствующем классе сущности или предоставить их через событие.
Настройте поля по умолчанию в вашем классе сущности.
class MyEntity extends \ElggObject {
/**
* {@inheritdoc}
*/
public static function getDefaultFields(): array {
return [
[
'#type' => 'text',
'#label' => elgg_echo('title'),
'name' => 'title',
'required' => true,
],
...
];
}
}
Вы также можете настроить свои поля в обратном вызове события или использовать обратный вызов для расширения других конфигураций полей.
/**
* Register the fields for my entity type/subtype
*
* @param \Elgg\Event $event 'fields', 'object:my_entity'
*
* @return array
*/
public function __invoke(\Elgg\Event $event): array {
$result = (array) $event->getValue();
$result[] = [
'#type' => 'text',
'#label' => elgg_echo('title'),
'name' => 'title',
'required' => true,
];
return $result;
}
Формы
Для вывода формы используйте функцию elgg_view_form следующим образом:
echo elgg_view_form('example');
Это генерирует разметку, подобную следующей:
<form action="http://localhost/elgg/action/example">
<fieldset>
<input type="hidden" name="__elgg_ts" value="1234567890" />
<input type="hidden" name="__elgg_token" value="3874acfc283d90e34" />
</fieldset>
</form>
Elgg автоматически делает для вас некоторые вещи, когда вы генерируете формы таким образом:
Он устанавливает action в соответствующий URL на основе имени действия, которое вы ему передаёте
Он добавляет некоторые anti-csrf токены (
__elgg_tsи__elgg_token), чтобы помочь защитить ваши действияОн автоматически ищет тело формы в представлении
forms/example.
Разместите содержимое вашей формы в представлении forms/example вашего плагина:
// /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'));
Теперь, когда вы вызываете elgg_view_form('example'), Elgg создаст:
<form action="http://localhost/elgg/action/example">
<fieldset>
<input type="hidden" name="__elgg_ts" value="...">
<input type="hidden" name="__elgg_token" value="...">
<input type="text" class="elgg-input-text" name="example">
<div class="elgg-foot elgg-form-footer">
<input type="submit" class="elgg-button elgg-button-submit" value="Submit">
</div>
</fieldset>
</form>
Поля ввода
Для отображения поля ввода формы используйте одно из встроенных представлений input, которые охватывают все стандартные элементы ввода HTML. См. отдельные файлы представлений для списка принимаемых параметров.
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',
));
Приведённый выше пример отобразит выпадающий список:
<select required="required" name="status" data-rel="blog" class="elgg-input-select">
<option value="draft">Draft</option>
<option value="published">Published</option>
</select>
Для обеспечения согласованности разметки полей используйте elgg_view_field(), который принимает все параметры отображаемого поля ввода, а также параметры #label и #help (оба из которых необязательны и принимают HTML или текст).
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',
]);
Приведённое выше сгенерирует следующую разметку:
<div class="elgg-field elgg-field-required">
<label for="elgg-field-1" class="elgg-field-label">Blog status<span title="Required" class="elgg-required-indicator">*</span></label>
<div class="elgg-field-input">
<select required="required" name="status" data-rel="blog" id="elgg-field-1" class="elgg-input-select">
<option value="draft">Draft</option>
<option value="published">Published</option>
</select>
</div>
<div class="elgg-field-help elgg-text-help">
<span class="elgg-icon-help elgg-icon"></span>This indicates whether or not the blog is visible in the feed
</div>
</div>
Типы полей ввода
Список встроенных типов/представлений input:
input/text— отображает текстовое поле ввода<input type="text">input/plaintext— отображает textarea<textarea></textarea>input/longtext— отображает текстовое поле ввода WYSIWYGinput/url— отображает поле ввода URL<input type="url">input/email— отображает поле ввода email<input type="email">input/checkbox— отображает одиночный чекбокс<input type="checkbox">input/checkboxes— отображает набор чекбоксов с одинаковым именемinput/radio— отображает один или несколько радиокнопок<input type="radio">input/submit— отображает кнопку отправки<button type="submit">input/button— отображает кнопку<button></button>input/file— отображает поле ввода файла<input type="file">input/select— отображает поле выбора<select></select>input/hidden— отображает скрытое поле ввода<input type="hidden">input/password— отображает поле ввода пароля<input type="password">input/number— отображает поле ввода числа<input type="number">input/date— отображает jQuery datepicker
Elgg предлагает некоторые вспомогательные типы полей ввода
input/access— отображает выбор уровня доступа Elgginput/tags— отображает поле ввода тегов Elgginput/autocomplete— отображает автодополнение сущностей Elgginput/captcha— представление-заполнитель для расширения плагинамиinput/friendspicker— отображает автодополнение друзей Elgginput/userpicker— отображает автодополнение пользователей Elgginput/grouppicker— отображает автодополнение групп Elgginput/objectpicker— отображает автодополнение объектов Elgginput/locationотображает поле ввода местоположения Elgg
Файлы и изображения
Используйте представление input/file в содержимом формы.
// /mod/example/views/default/forms/example.php
echo elgg_view('input/file', ['name' => 'icon']);
Если вы хотите загрузить иконку для сущности, вы можете использовать вспомогательное представление entity/edit/icon. Это представление показывает поле ввода файла для загрузки новой иконки для сущности, миниатюру текущей иконки и опцию удаления текущей иконки.
Представление поддерживает некоторые переменные для управления выводом
entity— сущность, для которой добавляется/удаляется иконка. Если предоставлено, на основе этой сущности будут показаны миниатюра и опция удаленияentity_type— тип сущности, для которой будет загружена иконка. Плагины могут найти это полезным, возможно, для проверки размеров иконокentity_subtype— подтип сущности, для которой будет загружена иконка. Плагины могут найти это полезным, возможно, для проверки размеров иконокicon_type— тип иконки (по умолчанию: icon)name— имя поля ввода/файла (по умолчанию: icon)remove_name— имя переключателя удаления иконки (по умолчанию: $vars[„name“] . „_remove“)required— требуется ли загрузка иконки (по умолчанию: false)cropper_enabled— разрешено ли обрезание иконки (по умолчанию: true)show_remove— показывать опцию удаления иконки (по умолчанию: true)show_thumb— показывать миниатюру сущности, если доступна (по умолчанию: true)thumb_size— размер иконки для использования в качестве миниатюры (по умолчанию: medium)
Если вы используете вспомогательное представление, вы можете использовать следующий код в вашем действии для сохранения иконки в сущность или удаления текущей иконки.
if (get_input('icon_remove')) {
$entity->deleteIcon();
} else {
$entity->saveIconFromUploadedFile('icon');
}
Установите enctype формы в multipart/form-data:
echo elgg_view_form('example', array(
'enctype' => 'multipart/form-data'
));
Примечание
enctype всех форм, использующих метод POST, по умолчанию равен multipart/form-data.
В вашем файле действия используйте elgg_get_uploaded_file('your-input-name') для доступа к загруженному файлу:
$icon = elgg_get_uploaded_file('icon');
Липкие формы
Липкие формы — это формы, которые сохраняют ввод пользователя, если сохранение не удалось. Они «липкие», потому что данные пользователя «прилипают» к форме после отправки, хотя они никогда не сохранялись в базе данных. Это значительно улучшает пользовательский опыт, минимизируя потерю данных. Elgg включает вспомогательные функции, чтобы вы могли сделать любую форму липкой.
Вспомогательные функции
Липкие формы реализованы в Elgg следующими функциями:
elgg_make_sticky_form($name)— сообщает движку сделать весь ввод в форме липким.elgg_clear_sticky_form($name)— сообщает движку отбросить весь липкий ввод в форме.elgg_is_sticky_form($name)— проверяет, является ли$nameдопустимой липкой формой.elgg_get_sticky_values($name)— возвращает все липкие значения, сохранённые для$nameфункциейelgg_make_sticky_form($name).
Обзор
Базовый поток использования липких форм:
Вызовите
elgg_make_sticky_form($name)в начале действий для форм, которые вы хотите сделать липкими.Используйте
elgg_is_sticky_form($name)иelgg_get_sticky_values($name)для получения липких значений при отображении представления формы.Вызовите
elgg_clear_sticky_form($name)после успешного завершения действия или после загрузки данных функциейelgg_get_sticky_values($name).
Примечание
Начиная с Elgg 5.0 формы, отображаемые с помощью elgg_view_form(), могут установить флаг $form_vars['sticky_enabled'] = true для автоматического получения поддержки липких форм. Отправленные значения в действие будут автоматически заполнены в $body_vars, когда в действии произошла ошибка.
elgg_view_form() поддерживает следующие $form_vars для помощи в поддержке липких форм:
sticky_enabled:boolдля включения автоматической поддержки липких формsticky_form_name: необязательнаяstringдля установки места сохранения значений липкой формы. По умолчанию это$action_nameи должно изменяться только если$action_nameотличается от фактического действияsticky_ignored_fields:arrayс именами полей формы, которые должны быть сохранены. Например, поля паролей
Пример: Регистрация пользователя
Простые липкие формы требуют мало логики для определения значений ввода для формы. Эта логика размещается в начале самого представления тела формы.
Представление формы регистрации сначала устанавливает значения по умолчанию для полей ввода, затем проверяет, есть ли липкие значения. Если да, оно загружает липкие значения перед очисткой липкой формы:
// views/default/forms/register.php
$password = $password2 = '';
$username = get_input('u');
$email = get_input('e');
$name = get_input('n');
if (elgg_is_sticky_form('register')) {
extract(elgg_get_sticky_values('register'));
elgg_clear_sticky_form('register');
}
Действие регистрации создаёт липкую форму и очищает её после завершения действия:
// actions/register.php
elgg_make_sticky_form('register', ['password', 'password2']);
elgg_register_user([
'username' => $username,
'password' => $password,
'name' => $name,
'email' => $email,
]);
elgg_clear_sticky_form('register');
Совет
Функция elgg_make_sticky_form() поддерживает необязательный второй аргумент $ignored_field_names. Это должен быть array с именами полей, которые вы не хотите делать липкими. Это полезно для полей, содержащих конфиденциальные данные, такие как пароли.
Пример: Закладки
Форма сохранения и действие встроенного плагина Bookmarks являются примером сложной липкой формы.
Представление формы для действия сохранения закладки использует elgg_extract() для извлечения значений из массива $vars:
// mod/bookmarks/views/default/forms/bookmarks/save.php
$title = elgg_extract('title', $vars, '');
$desc = elgg_extract('description', $vars, '');
$address = elgg_extract('address', $vars, '');
$tags = elgg_extract('tags', $vars, '');
$access_id = elgg_extract('access_id', $vars, ACCESS_DEFAULT);
$container_guid = elgg_extract('container_guid', $vars);
$guid = elgg_extract('guid', $vars, null);
$shares = elgg_extract('shares', $vars, array());
Скрипты обработчика страниц включают поддержку липких форм, передавая правильные значения в elgg_view_form():
// mod/bookmarks/pages/add.php
$content = elgg_view_form('bookmarks/save', ['sticky_enabled' => true]);
Аналогично, mod/bookmarks/pages/edit.php использует ту же поддержку липкости, но передаёт сущность, которая редактируется:
$bookmark_guid = get_input('guid');
$bookmark = get_entity($bookmark_guid);
...
$content = elgg_view_form('bookmarks/save', ['sticky_enabled' => true], ['entity' => $bookmark]);
Плагин имеет слушатель событий на событии 'form:prepare:fields', 'bookmarks/save', и обработчик делает 2 вещи:
Определяет имена полей ввода и значения по умолчанию для полей ввода формы.
Извлекает значения из объекта закладки, если он передан.
// mod/bookmarks/classes/Elgg/Bookmarks/Forms/PrepareFields.php
/**
* Prepare the fields for the bookmarks/save form
*
* @since 5.0
*/
class PrepareFields {
/**
* Prepare fields
*
* @param \Elgg\Event $event 'form:prepare:fields', 'bookmarks/save'
*
* @return array|null
*/
public function __invoke(\Elgg\Event $event): ?array {
$vars = $event->getValue();
// input names => defaults
$values = [
'title' => get_input('title', ''), // bookmarklet support
'address' => get_input('address', ''),
'description' => '',
'access_id' => ACCESS_DEFAULT,
'tags' => '',
'container_guid' => elgg_get_page_owner_guid(),
'guid' => null,
];
$bookmark = elgg_extract('entity', $vars);
if ($bookmark instanceof \ElggBookmark) {
// load current bookmark values
foreach (array_keys($values) as $field) {
if (isset($bookmark->$field)) {
$values[$field] = $bookmark->$field;
}
}
}
return array_merge($vars, $values);
}
}
Действию сохранения не нужно ничего делать с поддержкой липких форм, так как всё это обрабатывается системой.
Ajax
Смотрите Руководство по Ajax для инструкций по вызову действий из JavaScript.
Безопасность
Для повышения безопасности все действия требуют CSRF токен. Вызовы к URL действий, которые не включают токены безопасности, будут проигнорированы, и будет сгенерировано предупреждение.
Несколько представлений и функций автоматически генерируют токены безопасности:
elgg_view('output/url', array('is_action' => true));
elgg_view('input/securitytoken');
$url = elgg_add_action_tokens_to_url("http://localhost/elgg/action/example");
$url = elgg_generate_action_url('myplugin/myaction');
В редких случаях вам может потребоваться сгенерировать токены вручную:
$__elgg_ts = elgg()->csrf->getCurrentTime()->getTimestamp();
$__elgg_token = elgg()->csrf->generateActionToken($__elgg_ts);
Вы также можете получить доступ к токенам из JavaScript:
elgg.security.token.__elgg_ts;
elgg.security.token.__elgg_token;
Они периодически обновляются, поэтому всегда должны быть актуальными.
Токены безопасности
Иногда нам нужно передать данные через ненадёжную сторону или сгенерировать «неугадываемый токен» на основе некоторых данных. Отраслевой стандартный алгоритм HMAC — это правильный инструмент для этого. Он позволяет нам проверить, что полученные данные были сгенерированы нашим сайтом и не были изменены. Обратите внимание, что даже сильные хеш-функции, такие как SHA-2, не должны использоваться без HMAC для этих задач.
Elgg предоставляет elgg_build_hmac() для генерации и проверки кодов аутентификации сообщений HMAC, которые невозможно угадать без закрытого ключа сайта.
// generate a querystring such that $a and $b can't be altered
$a = 1234;
$b = "hello";
$query = http_build_query([
'a' => $a,
'b' => $b,
'mac' => elgg_build_hmac([$a, $b])->getToken(),
]);
$url = "action/foo?$query";
// validate the querystring
$a = (int) get_input('a', '', false);
$b = (string) get_input('b', '', false);
$mac = get_input('mac', '', false);
if (elgg_build_hmac([$a, $b])->matchesToken($mac)) {
// $a and $b have not been altered
}
Примечание: Если вы используете не-строку в качестве данных HMAC, вы должны последовательно использовать типы. Рассмотрим следующее:
$mac = elgg_build_hmac([123, 456])->getToken();
// type of first array element differs
elgg_build_hmac(["123", 456])->matchesToken($mac); // false
// types identical to original
elgg_build_hmac([123, 456])->matchesToken($mac); // true
Подписанные URL
Подписанные URL предлагают ограниченный уровень безопасности для ситуаций, когда токены действий не подходят, например, при отправке ссылки подтверждения по электронной почте. Подписи URL подтверждают, что URL был сгенерирован вашей установкой Elgg (с использованием секретного ключа сайта) и что элементы запроса URL не были изменены.
URL подписываются с помощью неугадываемого ключа HMAC SHA-256. См. Токены безопасности для получения дополнительной информации.
// inside a \Elgg\Notifications\NotificationEventHandler class
protected function getNotificationBody(\ElggUser $recipient, string $method): string {
$url = elgg_http_add_url_query_element(elgg_normalize_url('confirm'), [
'user_guid' => $recipient->guid,
]);
$url = elgg_http_get_signed_url($url);
return "Please confirm by clicking this link: $url";
}
Предупреждение
Подписанные URL не обеспечивают защиту от CSRF и не должны использоваться вместо токенов действий.