Добавляем в системный журнал функцию блокировки по ip
Код добавляет в записи системного журнала ссылку на блокировку адреса:
После клика на ссылке, адрес попадает в чёрный список (admin/user/rules).
/**
* Implements hook_menu().
*/
function mymodule_menu() {
$items = array();
$items['ban/%'] = array(
'page callback' => 'mymodule_ban_by_ip',
'page arguments' => array(1),
'access arguments' => array('administer permissions'),
'type' => MENU_CALLBACK,
);
return $items;
}
/**
* Implements template_preprocess_page().
*/
function mymodule_preprocess_page(&$vars) {
$item = menu_get_item();
if ($item['path'] == 'admin/reports/event/%') {
$link = '<a href="' . $GLOBALS['base_path'] . 'ban/$1?destination=' . $_GET['q'] . '">забанить</a>';
$vars['content'] = preg_replace(
'#<th>' . t('Hostname') . '</th><td>(.+?)</td>#',
'<th>' . t('Hostname') . '</th><td>$1 [ ' . $link . ' ]</td>',
$vars['content']
);
}
}
/**
* Ban user by IP.
*/
function mymodule_ban_by_ip($ip) {
if ($ip != ip_address()) {
db_query("INSERT INTO {access} (mask, type, status) VALUES ('%s', '%s', %d)", $ip, 'host', 0);
drupal_set_message('Адрес заблокирован');
}
drupal_goto();
}
Написанное актуально для
Drupal 6.x
Как из php скрипта асинхронно выполнить другой скрипт (т.е. не дожидаясь его окончания)
Например есть скрипт a.php, в котором нужно вызвать тяжеловесный скрипт b.php и не дожидаясь его окончания продолжить работу. Одно из решений это сокеты:
// a.php
function exec_script($url, $params = array())
{
$parts = parse_url($url);
if (!$fp = fsockopen($parts['host'], isset($parts['port']) ? $parts['port'] : 80))
{
return false;
}
$data = http_build_query($params, '', '&');
fwrite($fp, "POST " . (!empty($parts['path']) ? $parts['path'] : '/') . " HTTP/1.1\r\n");
fwrite($fp, "Host: " . $parts['host'] . "\r\n");
fwrite($fp, "Content-Type: application/x-www-form-urlencoded\r\n");
fwrite($fp, "Content-Length: " . strlen($data) . "\r\n");
fwrite($fp, "Connection: Close\r\n\r\n");
fwrite($fp, $data);
fclose($fp);
return true;
}
exec_script('http://example.com/b.php', array('foo' => 'bar'));
В примере, в скрипте b.php будет доступна переменная $_POST['foo'] со значением bar.
Как отдать пользователю данные в виде файла
В примере по кнопке "Экспорт" браузер предлагает сохранить текстовый файл состоящий из строчки Hello World!:
/**
* Реализация hook_menu()
*/
function mymodule_menu() {
$items['export'] = array(
'title' => 'Export',
'page callback' => 'drupal_get_form',
'page arguments' => array('mymodule_export_form'),
'access arguments' => array('administer site configuration'),
);
return $items;
}
/**
* Форма экспорта
*/
function mymodule_export_form() {
$form['submit'] = array(
'#type' => 'submit',
'#value' => 'Экспорт',
);
return $form;
}
/**
* Экспорт
*/
function mymodule_export_form_submit($form, &$form_state) {
$data = 'Hello World!';
header('Content-Disposition: attachment; filename=somename.txt');
header('Content-Type: text/plain; charset=utf-8');
echo $data;
exit;
}
Как отправить POST запрос
Drupal 6:
$result = drupal_http_request(
'http://example.com',
array('Content-Type' => 'application/x-www-form-urlencoded'),
'POST',
'param1=value1¶m2=value2'
);
Drupal 7:
$result = drupal_http_request('http://example.com', array(
'headers' => array('Content-Type' => 'application/x-www-form-urlencoded'),
'method' => 'POST',
'data' => 'param1=value1¶m2=value2',
));
Написанное актуально для
Drupal 6, Drupal 7
Как программно заполнить поле расширенного профиля (Profile)
Код, который заполняет поле profile_phone значением 8905 987-21-32:
$uid = 1; // id пользователя
$fieldName = 'profile_phone'; // имя поля
$fieldCategory = 'Контактная информация'; // категория поля
$fieldValue = '8905 987-21-32'; // новое значение поля
$field = db_fetch_object(db_query("
SELECT fid, type FROM {profile_fields}
WHERE name = '%s' AND category = '%s'
", $fieldName, $fieldCategory));
if (_profile_field_serialize($field->type)) {
$fieldValue = serialize($fieldValue);
}
db_query("
UPDATE {profile_values} SET value = '%s'
WHERE fid = %d AND uid = %d
", $fieldValue, $field->fid, $uid);
Написанное актуально для
Drupal 6.x
Как программно удалить ошибку валидации установленную с помощью form_set_error()
Бывает нужно избавиться от ошибки валидации, которая проходила в другом модуле. Код ниже, удаляет ошибку валидации поля some_field в форме some_form:
/**
* Реализация hook_form_FORM_ID_alter()
*/
function mymodule_form_some_form_alter(&$form) {
// вешаем на форму свой валидатор.
$form['#validate'][] = 'mymodule_some_form_validate';
}
/**
* Валидация формы some_form
*/
function mymodule_some_form_validate($form, &$form_state) {
$errors = form_set_error(); // получаем список ошибок
if (isset($errors['mail'])) {
// удаляем сообщение об ошибке
unset($_SESSION['messages']['error'][array_search($errors['mail'], $_SESSION['messages']['error'])]);
// удаляем саму ошибку
unset($errors['mail']);
// сбрасываем список ошибок
form_set_error(NULL, '', TRUE);
// заполняем список по новой
foreach ($errors as $key => $value) {
form_set_error($key, $value);
}
// удаляем дубли ошибок, добавленных прошлым шагом
$_SESSION['messages']['error'] = array_values(array_unique($_SESSION['messages']['error']));
}
}
Написанное актуально для
Drupal 6.x
Как добавить новое поле в существующую таблицу БД
Урезанный, но рабочий пример из модуля FileField Paths, который добавляет в таблицу files новое поле origname:
/**
* Implements hook_schema_alter().
*/
function filefield_paths_schema_alter(&$schema) {
$schema['files']['fields']['origname'] = array(
'description' => 'Original name of the file.',
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
'default' => '',
);
}
/**
* Implements hook_install().
*/
function filefield_paths_install() {
db_add_field($ret = array(), 'files', 'origname', array(
'description' => 'Original name of the file.',
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
'default' => '',
));
}
/**
* Implements hook_uninstall().
*/
function filefield_paths_uninstall() {
db_drop_field($ret = array(), 'files', 'origname');
}
Код находится в файле filefield_paths.install.
А вот более универсальный вариант для добавления нескольких полей:
/**
* Implements hook_schema_alter()
*/
function mymodule_schema_alter(&$schema) {
if (isset($schema['table_name'])) {
$schema['table_name']['fields']['field_1'] = array(
'type' => 'varchar',
'length' => 100,
'not null' => TRUE,
'default' => '',
);
$schema['table_name']['fields']['field_2'] = array(
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'size' => 'tiny',
'default' => 0,
);
}
}
/**
* Implements hook_install()
*/
function mymodule_install() {
$schema = array('table_name' => array());
mymodule_schema_alter($schema);
foreach ($schema['table_name']['fields'] as $field => $spec) {
db_add_field($ret = array(), 'table_name', $field, $spec);
}
}
/**
* Implements hook_uninstall()
*/
function mymodule_uninstall() {
$schema = array('table_name' => array());
mymodule_schema_alter($schema);
foreach ($schema['table_name']['fields'] as $field => $spec) {
db_drop_field($ret = array(), 'table_name', $field);
}
}
Написанное актуально для
Drupal 6.x
Как получить доступ к доп. полям профиля пользователя
global $user;
profile_load_profile($user);
$phone = $user->profile_phone;
Ф-я profile_load_profile() добавляет в объект $user значения кастомных полей, созданных с помощью модуля Profile.
Написанное актуально для
Drupal 6.x
Как правильно делать JOIN для таблиц с CCK полями
Если вам захочется сделать выборку нод вместе с CCK полями, то первым желанием будет посмотреть схему таблиц и набросать что-нибудь в духе:
SELECT n.title, f.field_name_value FROM {node} n
INNER JOIN {content_type_story} f ON n.nid = f.nid AND n.vid = f.vid
WHERE n.type = 'story'
ORDER BY n.title
(в примере выбираются заголовки нод типа story и соответствующее им значение cck поля field_name)
Однако тут есть подводный камень — если поле используется только в одном типе материалов и может иметь только одно значение, то оно хранится в таблице с именем content_type_TYPE (где TYPE это тип материала), иначе же значения хранятся в таблице content_FIELD_NAME (где FIELD_NAME это название поля). Это значит, что как только мы добавим поле field_name в другой материал, или дадим ему возможность иметь несколько значений, запрос перестанет работать.
Поэтому правильней будет — сначала с помощью CCK API узнать название таблицы, и только потом джойнить её в SQL запросе:
$field = content_fields('field_name');
$db_info = content_database_info($field);
$result = db_query("
SELECT title, %s FROM {node} n
INNER JOIN {%s} f ON n.nid = f.nid AND n.vid = f.vid
WHERE n.type = 'story'
ORDER BY n.title
", $db_info['columns']['value']['column'], $db_info['table']);
Написанное актуально для
CCK 6.x-2.x
Как из Javascript-a подключить Lightbox к определённым элементам
Код заставляет открываться в Lightbox2 все ссылки с вложенным тегом <img />:
Drupal 6:
$(function(){
$('a:has(img)').attr('rel', 'lightbox');
Lightbox.initList();
});
Drupal 7:
jQuery(function(){
jQuery('a:has(img)').attr('rel', 'lightbox');
Lightbox.initList();
});
Как создать пэйджер для данных не из БД
В примере создаётся пэйджер на 5 страниц без использования функции pager_query():
global $pager_page_array, $pager_total;
// настройки пейджера
$per_page = 10; // строк на страницу
$total_rows = 50; // всего строк
$element = 0; // номер пэйджера
// инициализация пэйджера
$pager_page_array[$element] = isset($_GET['page']) ? $_GET['page'] : 0;
$pager_total[$element] = ceil($total_rows / $per_page);
// генерация пэйджера
$pager = theme('pager', null, $per_page, $element);
Написанное актуально для
Drupal 6.x
Как переопределить функцию темизации в своём модуле
Есть два способа переопределения функций темизаций в модуле. Первый, это реализация хука hook_theme:
/**
* Реализация hook_theme()
* Переопределение функции theme_breadcrumb()
*/
function mymodule_theme() {
return array(
'breadcrumb' => array(
'function' => 'mymodule_breadcrumb',
'arguments' => array('breadcrumb' => NULL),
),
);
}
/**
* Функция темизации хлебных крошек
*/
function mymodule_breadcrumb($breadcrumb) {
if (!empty($breadcrumb)) {
return '<div class="breadcrumb">' . implode(' → ', $breadcrumb) . '</div>';
}
}
этот код будет работать только если функция theme_breadcrumb не переопределена в template.php или в модуле с бОльшим весом.
Следующий код будет работать даже если функция переопределена в template.php
/**
* Реализация hook_theme_registry_alter()
* Переопределение функции theme_breadcrumb()
*/
function mymodule_theme_registry_alter(&$theme_registry) {
$theme_registry['breadcrumb']['function'] = 'mymodule_breadcrumb';
}
/**
* Функция темизации хлебных крошек
*/
function mymodule_breadcrumb($breadcrumb) {
if (!empty($breadcrumb)) {
return '<div class="breadcrumb">' . implode(' → ', $breadcrumb) . '</div>';
}
}
Написанное актуально для
Drupal 6.x
Уведомление администратора о новых комментариях в семёрке
Код отправляет уведомление о новых комментариях на ящик admin_mail@gmail.com
/**
* Implements hook_comment_insert().
*/
function mymodule_comment_insert($comment) {
if ($comment->uid != 1) {
drupal_mail('mymodule', 'notify_of_new_comment', 'admin_mail@gmail.com', language_default(), $comment);
}
}
/**
* Implements hook_mail().
*/
function mymodule_mail($key, &$message, $params) {
if ($key == 'notify_of_new_comment') {
$node = node_load($params->nid);
$body = $params->name . ' оставил комментарий в теме "' . $node->title . '":' . "\n\n";
$body .= trim($params->comment_body['und'][0]['value']) . "\n\n";
$body .= url('node/' . $params->nid, array('absolute' => true));
$message['subject'] = 'Уведомление о новом комментарии';
$message['body'][] = $body;
}
}
Написанное актуально для
Drupal 7
Добавляем контекстные ссылки для комментариев
В семёрке контекстные ссылки (contextual links) есть у материалов и блоков, но отчего-то их нет у комментариев.
Код ниже добавляет к каждому комментарию ссылку на редактирование и удаление:
/**
* Реализация hook_menu_alter()
*/
function mymodule_menu_alter(&$items)
{
$items['comment/%comment/edit']['context'] = MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE;
$items['comment/%/delete']['context'] = MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE;
}
/**
* Реализация hook_comment_view()
*/
function mymodule_comment_view($comment, $view_mode, $langcode)
{
if ($view_mode == 'full')
{
$comment->content['#contextual_links']['comment'] = array('comment', array($comment->cid));
}
}
Обычные ссылки за ненадобностью можно скрыть — в template.php добавить:
function THEMENAME_preprocess_comment(&$vars)
{
hide($vars['content']['links']);
}
Не забываем сбросить кэш.
Написанное актуально для
Drupal 7.x
Как программно создать блок
Пример создания простейшего блока в Drupal 7:
/**
* Реализация hook_block_info().
* Информация о блоке и дефолтные настройки.
*/
function mymodule_block_info() {
$blocks['hello-message'] = array('info' => t('Message'));
return $blocks;
}
/**
* Реализация hook_block_view().
* Генерация заголовка блока и его контента.
*/
function mymodule_block_view($delta = '') {
$block = array();
if ($delta == 'hello-message') {
$block['subject'] = t('Message');
$block['content'] = t('Hello World!');
}
return $block;
}
Пример создания простейшего блока в Drupal 6:
/**
* Реализация hook_block().
*/
function mymodule_block($op = 'list', $delta = 0, $edit = array()) {
if ($op == 'list') {
$blocks[0] = array('info' => 'Message');
return $blocks;
}
else if ($op == 'view') {
if ($delta == 0) {
$block = array(
'subject' => t('Message'),
'content' => t('Hello World!'),
);
}
return $block;
}
}
Написанное актуально для
Drupal 6, Drupal 7