Отключить кэш страниц для определённых ip адресов

Основная сложность в том, что если аноним заходит на закэшированную страницу, то нет никакой возможности с помощью API повлиять на результат. Поэтому приходится делать проверку на самом раннем этапе бутстрапа — инициализации конфига.

Соответственно, чтобы отключить кэш страниц для определённых ip адресов, нужно в settings.php добавить:

if (in_array($_SERVER['REMOTE_ADDR'], array('1.2.3.4', '5.6.7.8', ...))) {
  $conf['cache'] = FALSE;
}

Написанное актуально для
Drupal 6

Добавлено: 30 Июля 2018 07:22:12 Добавил: Андрей Ковальчук

Пишем вычисляемое поле для Views 2

Вычисляемое поле (или виртуальное поле, пользовательское поле, computed field, calculated field) — это поле, которого физически нет в таблице, и значение которого вычисляется на основании других полей или подзапроса.

Добавить такое поле в представление Views можно несколькими способами, но drupal way — это написать свой хэндлер (field handler).

Для примера создадим поле, которое будет выводить количество файлов, прикреплённых к материалу с помощью модуля FileField.

1. В основном файле модуля реализуем хук hook_views_api():

/**
 * Implements hook_views_api()
 */
function MODULENAME_views_api() {
  return array('api' => 2);
}

2. В папке модуля создаём файл MODULENAME.views.inc и реализуем в нём хук hook_views_data(), в котором описываем новое поле:

/**
 * Implements hook_views_data()
 */
function MODULENAME_views_data() {
  return array(
    'views' => array(                                                // виртуальная таблица модуля Views
      'files_count' => array(                                        // системное название виртуального поля
        'title' => t('Files count'),                                 // человекопонятное название поля
        'help' => t('Number of attached files'),                     // описание поля
        'group' => t('Content'),                                     // группа, в которой будет поле
        'field'  => array(
          'handler' => 'MODULENAME_views_handler_field_files_count', // хэндлер поля
          'click sortable' => TRUE,                                  // включаем возможность пользовательской сортировки по этому полю
        ),
      ),
    ),
  );
}

3. В этом же файле реализуем хук hook_views_handlers(), в котором указываем базовую информацию о хэндлере:

/**
 * Implements hook_views_handlers()
 */
function MODULENAME_views_handlers() {
  return array(
    'handlers' => array(
      'MODULENAME_views_handler_field_files_count' => array(
        'parent' => 'views_handler_field_numeric',
        'file' => 'MODULENAME_views_handler_field_files_count.inc',
      ),
    ),
  );
}

4. В папке модуля создаём файл MODULENAME_views_handler_field_files_count.inc и реализуем в нём класс хэндлера, унаследованный от views_handler_field_numeric:

class MODULENAME_views_handler_field_files_count extends views_handler_field_numeric {
  function query() {
    $this->field_alias = $this->query->add_field(
      NULL,
      '(SELECT COUNT(*) FROM {content_field_files} cff WHERE cff.nid = node.nid)',
      'files_count'
    );
  }
 
  function click_sort($order) {
    $this->query->add_orderby(NULL, NULL, $order, $this->field_alias);
  }
}

В методе query() добавляется подзапрос, который возвращает количество файлов для каждого материала.

В методе click_sort() добавляется возможность пользовательской сортировки по виртуальному полю (родительский метод не умеет делать сортировку по синониму).

На этом всё. Сбрасываем кэш, создаём представление, добавляем поля, добавляем новое поле Files count:

Написанное актуально для
Views 6.x-2.x

Добавлено: 30 Июля 2018 07:21:32 Добавил: Андрей Ковальчук

Как удалить все материалы определённого типа

Пример удаления всех материалов с типом article:

drupal_set_time_limit(0);
$nids = db_select('node', 'n')
  ->fields('n', array('nid'))
  ->condition('n.type', 'article')
  ->execute()
  ->fetchCol();
foreach ($nids as $nid) {
  node_delete($nid);
}

Код можно выполнить с помощью Devel, по адресу devel/php

Похожий способ, только с применением EntityFieldQuery.

Написанное актуально для
Drupal 7

Добавлено: 30 Июля 2018 07:19:21 Добавил: Андрей Ковальчук

Включаем/выключаем набор модулей одним кликом

Держать включёнными девелоперские модули на продакшене не очень разумно, но иногда возникает необходимость что-нибудь быстро подправить в представлениях или полях. Поэтому удобно иметь инструмент, который бы включал/выключал заранее определённый набор модулей одним кликом. Таким инструментом как неудивительно является модуль Admin Menu:

Список модулей прописывается в settings.php в формате:

$conf['admin_menu_devel_modules'] = array(
  'modulename1',
  'modulename2',
  'modulenameN',
);

Например:
$conf['admin_menu_devel_modules'] = array(
  'devel',
  'field_ui',
  'l10n_update',
  'module_filter',
  'update',
  'views_ui',
);

Написанное актуально для
Administration Menu 7.x-3.0-rc1

Добавлено: 30 Июля 2018 07:18:37 Добавил: Андрей Ковальчук

Изменяем дефолтное значение поля Term reference на значение из последнего созданного материала

При наполнение какого-нибудь каталога, основанного на таксономии, в форме создания материала удобно изменять дефолтное значение поля таксономии на значение из предыдущего созданного материала. Например создав материал с термином "Горячие блюда" и открыв форму добавления нового материала, в поле будет выбрано не значение указанное в настройках поля, а термин "Горячие блюда":

/**
 * Implements hook_form_alter()
 */
function MODULENAME_form_alter(&$form, &$form_state, $form_id) {
  if ($form_id == 'NODETYPE_node_form' && !$form['nid']['#value']) {
    $last_tid = db_select('field_data_FIELDNAME', 'f')
      ->fields('f', array('FIELDNAME_tid'))
      ->condition('f.entity_type', 'node')
      ->condition('f.bundle', 'NODETYPE')
      ->orderBy('f.entity_id', 'DESC')
      ->range(0, 1)
      ->execute()
      ->fetchField();
    $lang = $form['FIELDNAME']['#language'];
    $form['FIELDNAME'][$lang]['#default_value'][0] = $last_tid;
  }
}

Вместо MODULENAME прописать имя модуля, вместо NODETYPE — тип материала, вместо FIELDNAME — имя поля.

Код работает для виджета Select. С другими не пробовал.

Написанное актуально для
Drupal 7

Добавлено: 30 Июля 2018 07:17:30 Добавил: Андрей Ковальчук

Показать результат выполнения AJAX запроса в jQuery UI Dialog

Пример формы, в которой после нажатия кнопки, юзер увидит jQuery UI Dialog с приветственным сообщением, отправленным из ajax callback-а:

function mymodule_test_form() {
  $form['submit'] = array(
    '#type' => 'button',
    '#value' => 'Open dialog',
    '#ajax' => array('callback' => 'mymodule_test_form_ajax_callback'),
  );
 
  $form['#attached']['library'][] = array('system', 'ui.dialog');
 
  return $form;
}
 
function mymodule_test_form_ajax_callback() {
  $commands[] = ajax_command_append('body', '<div id="dialog" class="element-hidden">Hello world</div>');
  $commands[] = ajax_command_invoke('#dialog', 'dialog');
 
  return array('#type' => 'ajax', '#commands' => $commands);
}

Написанное актуально для
Drupal 7

Добавлено: 30 Июля 2018 07:16:00 Добавил: Андрей Ковальчук

Человечные заголовки у форм создания материалов

По умолчанию формы создания материалов озаглавлены по шаблону "Создание материала

". Код ниже, позволяет назначить каждому типу свой человекопонятный заголовок:
[PHP]/**
 * Preprocess function for page.tpl.php.
 */
function MODULENAME_preprocess_page() {
  if (arg(0) == 'node' && arg(1) == 'add' && arg(2)) {
    $titles = array(
      'page'     => 'Добавление страницы',
      'article'  => 'Добавление статьи',
      'feedback' => 'Добавление отзыва',
    );
    if (isset($titles[arg(2)])) {
      drupal_set_title($titles[arg(2)]);
    }
  }
}

Написанное актуально для
Drupal 7

Добавлено: 30 Июля 2018 07:13:17 Добавил: Андрей Ковальчук

Необязательное поле загрузки файлов

Ф-я file_save_upload() возвращает три типа значения:
— объект, если файл загружен успешно
— FALSE, если во время загрузки произошла ошибка
— NULL, если файл не был выбран.

Соответственно код:

function mymodule_form($form, &$form_state) {
  $form['upload_file'] = array(
    '#type' => 'file',
    '#title' => 'File',
  );
  $form['submit'] = array(
    '#type' => 'submit',
    '#title' => 'Upload',
  );
  return $form;
}
 
function mymodule_form_validate($form, &$form_state) {
  $file = file_save_upload('upload_file', array(), 'temporary://');
  if ($file !== NULL) {
    if ($file) {
      $form_state['values']['upload_file'] = $file;
    }
    else {
      form_set_error('file', 'File could not be uploaded');
    }
  }
}

Написанное актуально для
Drupal 7

Добавлено: 29 Июля 2018 22:47:43 Добавил: Андрей Ковальчук

Как для определённой роли спрятать колонку или поле в представлении Views

Например нужно спрятать поле field_secret для анонимных пользователей (id роли: 1) в представлении myview дисплея page_1:

Views 3

/**
 * Implements hook_views_pre_render().
 */
function mymodule_views_pre_render(&$view) {
  if (
    $view->name == 'myview' &&
    $view->current_display == 'page_1' &&
    isset($GLOBALS['user']->roles[1])
  ) {
    $view->field['field_secret']->options['exclude'] = TRUE;
  }
}

Views 2
/**
 * Implements hook_views_pre_render().
 */
function mymodule_views_pre_render(&$view) {
  if (
    $view->name == 'myview' &&
    $view->current_display == 'page_1' &&
    isset($GLOBALS['user']->roles[1])
  ) {
    unset($view->field['field_secret']);
  }
}

Написанное актуально для
Views 2, Views 3

Добавлено: 29 Июля 2018 22:46:15 Добавил: Андрей Ковальчук

Как программно создать комментарий

Пример программного создания комментария для ноды 123:

global $user;
 
$comment = array(
  'nid' => 123,
  'pid' => 0,
  'uid' => $user->uid,
  'name' => $user->name,
  'author' => $user->name,
  'subject' => 'Comment subject',
  'comment' => 'Comment body',
);
$form_state = array();
$form_state['values'] = $comment;
$form_state['values']['op'] = t('Save');
 
drupal_execute('comment_form', $form_state, $comment);

Как программно отправить любую форму.

Написанное актуально для
Drupal 6

Добавлено: 29 Июля 2018 22:44:01 Добавил: Андрей Ковальчук

Как обойти все элементы формы

Пример отключения Wysiwyg редактора для всех полей типа text_format в форме создания/редактирования материала типа page:

/**
 * Implements hook_form_FORM_ID_alter().
 */
function mymodule_form_page_node_form_alter(&$form) {
  mymodule_disable_wysiwyg($form);
}
 
/**
 * Disable wysiwyg in all fields
 */
function mymodule_disable_wysiwyg(&$form) {
  foreach (element_children($form) as $element) {
    if (isset($form[$element]['#type']) && $form[$element]['#type'] == 'text_format') {
      $form[$element]['#wysiwyg'] = FALSE;
    }
    else {
      mymodule_disable_wysiwyg($form[$element]);
    }
  }
}

Написанное актуально для
Drupal 7

Добавлено: 29 Июля 2018 22:38:58 Добавил: Андрей Ковальчук

Как вывести список в две колонки

Две колонки

PHP:

// демо данные
$items = array(
  'Item 1, col 1',
  'Item 2, col 1',
  'Item 3, col 1',
  'Item 4, col 2',
  'Item 5, col 2',
  'Item 6, col 2',
);
 
// разбиваем список на две части
$cols = array_chunk($items, round(count($items) / 2));
 
// выводим враппер и списки
echo '<div class="two-cols">';
foreach ($cols as $items) {
  echo theme('item_list', array('items' => $items));
}
echo '</div>';

CSS:
.two-cols .item-list {
  float: left;
  width: 50%;
}

Написанное актуально для
Drupal 7

Добавлено: 29 Июля 2018 22:38:20 Добавил: Андрей Ковальчук

Переместить заголовок страницы в блок content

В семёрке контент является блоком, а его заголовок почему то отдельным элементом в page.tpl.php. Чтобы это исправить, нужно во первых, добавить в template.php:

/**
 * Реализация hook_preprocess_block().
 */
function [THEMENAME]_preprocess_block(&$vars) {
  if ($vars['block_html_id'] == 'block-system-main') {
    $vars['elements']['#block']->subject = drupal_get_title();
  }
}

а во вторых, удалить из page.tpl.php вывод переменной $title.

Написанное актуально для
Drupal 7

Добавлено: 29 Июля 2018 22:37:24 Добавил: Андрей Ковальчук

Как в Ubercart добавить товар в корзину с помощью AJAX

Пример добавления в корзину товара с nid 123.

Javascript:

// Посылаем AJAX запрос
$.post(Drupal.settings.basePath + 'ajax-add-to-cart', {nid:123}, function(response) {
  response = Drupal.parseJson(response);
 
  // Показываем сообщение если что-то пошло не так
  if (!response.status) {
    return alert(response.data);
  }
 
  // Обновляем блок с корзиной
  $('#block-uc_cart-0 .content').html(response.data);
});

PHP:
/**
 * Реализация hook_menu()
 */
function mymodule_menu() {
  $items['ajax-add-to-cart'] = array(
    'page callback' => 'mymodule_ajax_add_to_cart',
    'access arguments' => array('access content'),
    'type' => MENU_CALLBACK,
  );
  return $items;
}
 
/**
 * Menu callback
 */
function mymodule_ajax_add_to_cart() {
  if (!isset($_POST['nid'])) {
    return;
  }
  // Добавляем товар в корзину
  uc_cart_add_item((int)$_POST['nid'], 1, NULL, NULL, FALSE);
  // Получаем содержимое блока корзины
  $block = module_invoke('uc_cart', 'block', 'view', 0);
  // Возвращаем содержимое блока корзины
  drupal_json(array('status' => TRUE, 'data' => $block['content']));
}

Написанное актуально для
Ubercart 6.x-2.x

Добавлено: 29 Июля 2018 22:35:13 Добавил: Андрей Ковальчук

Как программно создать синоним URL для ноды

Пример добавления для ноды 123 синонима my-path-alias.

Способ 1:

$node = node_load(123);
$node->path = array(
  'alias' => 'my-path-alias',
  'language' => $node->language,
);
node_save($node);

Способ 2:
$path = array(
  'alias' => 'my-path-alias',
  'source' => 'node/123',
  'language' => LANGUAGE_NONE,
);
path_save($path);

Написанное актуально для
Drupal 7

Добавлено: 29 Июля 2018 22:34:28 Добавил: Андрей Ковальчук