Сохранение файлов, загруженных с помощью One Click Upload, в File/Image field

Одной из причин, по которой некоторые обходили стороной модуль One Click Upload, это загрузка файлов "в никуда". Т.е. загруженные файлы никак не связанны с тем содержимым, в котором они выводятся и нет способа автоматически удалять эти файлы при удалении содержимого.

С помощью последней dev версии модуля и небольшого количества кода, можно сохранять файлы в любое file или image поле.

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

/**
 * Implements hook_ocupload_files_saved().
 */
function MODULENAME_ocupload_saved_data_alter(&$files, &$form, &$form_state) {
  if (
    isset($form['#entity_type']) &&
    in_array($form['#entity_type'], array('node', 'comment')) &&
    isset($form_state['values']['field_inline_images'])
  ) {
    foreach ($files as $file) {
      $form_state['values']['field_inline_images']['und'][] = array(
        'fid' => $file->fid,
        '_weight' => 100,
      );
    }
  }
}

Замечание: опция Delete unused files в настройках модуля должна быть включена.

Написанное актуально для
One Click Upload 7.x-1.x-dev

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

Собственное событие для Rules 2

Создание своего event-a для Rules состоит из двух шагов — реализация hook_rules_event_info() и вызов rules_invoke_event().

Пример события, которое возникает после смены имени пользователя:

/**
 * Implements hook_rules_event_info().
 */
function modulename_rules_event_info() {
  $items = array(
    'user_name_changed' => array(
      'label' => t('After changing user name'),
      'group' => t('User'),
      'variables' => array(
        'user' => array(
          'type' => 'user',
          'label' => t('user'),
        ),
      ),
    ),
  );
  return $items;
}
 
/**
 * Implements hook_entity_update().
 */
function modulename_entity_update($entity, $type) {
  if ($type == 'user' && $entity->name != $entity->original->name) {
    rules_invoke_event('user_name_changed', $entity);
  }
}

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

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

Очистить get форму от лишних параметров (form_build_id, form_token, form_id, op)

Если нужно отправить форму методом get на внешний адрес и при этом не хочется светить form_build_id, form_token, form_id и op:

/**
 * Form
 */
function mymodule_myform($form, &$form_state) {
  $form['#action'] = 'http://example.com/path';
  $form['#method'] = 'GET';
  $form['#pre_render'][] = 'mymodule_myform_pre_render';
 
  ...
 
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Submit'),
    '#name' => '',
  );
 
  return $form;
}
 
/**
 * Form pre render callback.
 */
function mymodule_myform_pre_render($form) {
  unset($form['form_token']);
  unset($form['form_build_id']);
  unset($form['form_id']);
  return $form;
}

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

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

Вывести регион в node.tpl.php

Пример вывода региона before_comments перед комментариями:

1. Добавить в THEMENAME.info:

regions[before_comments] = Before comments

2. Добавить в template.php:

/**
 * Preprocess function for node.tpl.php.
 */
function THEMENAME_preprocess_node(&$vars) {
  $vars['region']['before_comments'] = block_get_blocks_by_region('before_comments');
}

3. В node.tpl.php, перед выводом $content['comments'] добавить:

<?php echo render($region['before_comments']); ?>

4. Сбросить кэш.

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

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

Узнать следующий/предыдущий термин

Задача — зная tid термина, узнать следующий и предыдущий термин.

Решение:

/**
 * Return prev/next term.
 */
function helper_get_sibling_term($tid, $type) {
  $term = taxonomy_term_load($tid);
  if ($type == 'next') {
    $operator = '>';
    $direction = 'ASC';
  }
  else {
    $operator = '<';
    $direction = 'DESC';
  }
  return db_select('taxonomy_term_data', 'td')
    ->fields('td', array('tid', 'name'))
    ->condition('td.vid', $term->vid)
    ->where(
      "td.weight $operator :weight OR (td.weight = :weight AND td.name $operator :name)",
      array(':weight' => $term->weight, ':name' => $term->name)
    )
    ->orderBy('td.weight', $direction)
    ->orderBy('td.name', $direction)
    ->range(0, 1)
    ->execute()
    ->fetchObject();
}

Использование:
$prev_term = helper_get_sibling_term(123, 'prev');
$next_term = helper_get_sibling_term(123, 'next');

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

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

Показать процесс загрузки ajax таба в Quicktabs 7.x-3.x

В версии же для Drupal 7 процесс загрузки показывается с помощью throbber-а возле активного таба, что по моему ужасно (контент при этом просто пропадает, а табы скачут)

Делаем по-старому:

1. Добавляем в js файл темы:

(function ($) {
  Drupal.behaviors.THEMENAME= {
    attach: function (context, settings) {
      $('.quicktabs-tabs a:not(.quicktabs-loaded)', context).click(function() {
        if ($(this).hasClass('progress-disabled')) {
          $(this).closest('.quicktabs-wrapper').addClass('quicktabs-loading');
        }
      });
      if ($(context).hasClass('quicktabs-tabpage')) {
        $(context).closest('.quicktabs-wrapper').removeClass('quicktabs-loading');
      }
    }
  };
})(jQuery);

2. Добавляем в css файл темы:

.quicktabs-tabs .ajax-progress {
  display: none;
}
.quicktabs-loading .quicktabs_main {
  height: 30px;
  margin-top: 20px;
  background: url(/misc/progress.gif);
}

Написанное актуально для
Quicktabs 7.x-3.x

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

Программно вывести свой контент в вкладках Quicktabs

Пример вывода двух вкладок Quicktabs с произвольным контентом:

Drupal 7:

$custom_tabs = array(
  array('title' => 'Tab 1', 'contents' => 'Content for tab 1'),
  array('title' => 'Tab 2', 'contents' => 'Content for tab 2'),
);
echo drupal_render(quicktabs_build_quicktabs('my_custom_tabs', array(), $custom_tabs));

Drupal 6:
$tabs = array(
  'tab_1' => array('type' => 'freetext', 'title' => 'Tab 1', 'text' => 'Content for tab 1'),
  'tab_2' => array('type' => 'freetext', 'title' => 'Tab 2', 'text' => 'Content for tab 2'),
);
echo theme('quicktabs', array(
  'qtid' => 'my_custom_tabs',
  'tabs' => $tabs,
  'style' => variable_get('quicktabs_tabstyle', 'nostyle'),
  'ajax' => FALSE,
));

Написанное актуально для
Quicktabs 3.x

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

Уникальные классы пунктов меню

У системных меню, выведенных в page.tpl.php с помощью theme_links() есть приятная особенность — все пункты обладают уникальным css классом на основе их идентификатора. В меню же, выводимых модулем Block, таких классов увы нет. Решаем проблему:

// template.php
 
/**
 * Implements hook_preprocess_menu_link().
 */
function THEMENAME_preprocess_menu_link(&$vars) {
  $vars['element']['#attributes']['class'][] = 'menu-item-' . $vars['element']['#original_link']['mlid'];
}

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

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

Программно получить список товаров в корзине Drupal Commerce

Пример получения идентификаторов товаров в корзине текущего пользователя:

$product_ids = array();
if (($order_id = commerce_cart_order_id($GLOBALS['user']->uid)) != FALSE) {
  $order = entity_metadata_wrapper('commerce_order', $order_id);
  foreach ($order->commerce_line_items as $line_item) {
    $product_ids[] = $line_item->commerce_product->product_id->value();
  }
}

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

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

Вывести в блоке погоду в Москве на основе данных яндекса

Пример модуля для вывода в блоке текущей погоды в Москве:

Пример блока

/**
 * Implements hook_block_info().
 */
function weather_block_info() {
  return array(
    'weather' => array(
      'info' => 'Погода в Москве',
      'cache' => DRUPAL_NO_CACHE,
    ),
  );
}
 
/**
 * Implements hook_block_view().
 */
function weather_block_view($delta = '') {
  $block = array();
  if ($delta == 'weather') {
    $weather = weather_get_weather();
    $block['subject'] = 'Погода в Москве';
    $block['content'] = '
      <img src="http://img.yandex.net/i/wiz' . $weather['image'] . '.png" alt="' . $weather['type'] . '" />
      ' . ($weather['temperature'] > 0 ? '+' . $weather['temperature'] : $weather['temperature']) . '
    ';
  }
  return $block;
}
 
/**
 * Return weather.
 */
function weather_get_weather($ignore_cache = FALSE) {
  if (!$ignore_cache && ($cache = cache_get('weather'))) {
    $weather = $cache->data;
  }
  else {
    $xml = simplexml_load_file('http://export.yandex.ru/weather-ng/forecasts/27530.xml');
    $weather = array(
      'temperature' => (string)$xml->fact->temperature,
      'image' => (string)$xml->fact->image,
      'type' => (string)$xml->fact->weather_type,
    );
    cache_set('weather', $weather);
  }
 
  return $weather;
}
 
/**
 * Implements hook_cron().
 */
function weather_cron() {
  if (REQUEST_TIME - variable_get('cron_last') > 60*60) {
    weather_get_weather(TRUE);
  }
}

Информация о погоде обновляться каждый час при ближайшем запуске крона.

Чтобы вывести погоду для другого города, достаточно в адресе xml-ки заменить 27530 на id города.

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

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

Программно добавить поле в представление

Программное добавление полей в представление articles с дисплеем page:

/**
 * Implements hook_views_pre_view().
 */
function MODULENAME_views_pre_view(&$view) {
  if ($view->name == 'articles' && $view->current_display == 'page') {
    // Добавление заголовка материала
    $view->add_item($view->current_display, 'field', 'node', 'title', array(
      'label' => 'Title',
    ), 'node_title');
 
    // Добавление поля типа Custom text
    $view->add_item($view->current_display, 'field', 'views', 'nothing', array(
      'label' => 'My custom field',
      'alter' => array('text' => 'My custom field text'),
      'element_class' => 'my-custom-field',
      'element_default_classes' => 0,
    ), 'my_custom_field');
  }
}

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

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

Удаляем друпаловский ресайзер textarea (grippie)

Переопределённый шаблон вывода textarea из которого удалены все упоминания grippie:

// template.php
 
/**
 * Override theme_textarea().
 */
function THEMENAME_textarea($vars) {
  $element = $vars['element'];
  element_set_attributes($element, array('id', 'name', 'cols', 'rows'));
  _form_set_class($element, array('form-textarea'));
  return '<textarea' . drupal_attributes($element['#attributes']) . '>' . check_plain($element['#value']) . '</textarea>';
}

Используется в моей базовой теме.

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

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

Показывать блок только на страницах терминов определённого словаря

Пример вывода блока только на страницах термина словаря tags:

<?php
if (
  arg(0) == 'taxonomy' &&
  arg(1) == 'term' &&
  ($term = taxonomy_term_load(arg(2))) &&
  $term->vocabulary_machine_name == 'tags'
) {
  return TRUE;
}
?>

Код прописывается в настройках видимости блока, с включённой опцией Pages on which this PHP code returns TRUE (experts only):

Модуль для вывода блока только на странице определённых терминов — Block Visibility by Term.

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

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

AJAX кнопка &quot;Добавить в корзину&quot; в Drupal Commerce

Код, позволяющий добавлять товары в корзину с помощью AJAX:

/**
 * Implements hook_form_FORM_ID_alter(): commerce_cart_add_to_cart_form
 */
function modulename_form_commerce_cart_add_to_cart_form_alter(&$form, &$form_state) {
  $form['submit']['#ajax'] = array('callback' => 'modulename_add_to_cart_ajax_callback');
  $form['#submit'][] = 'modulename_add_to_cart_form_submit';
}
 
/**
 * "Add to cart" button ajax callback.
 */
function modulename_add_to_cart_ajax_callback($form, &$form_state) {
  drupal_get_messages();
  $cart_block = module_invoke('commerce_cart', 'block_view', 'cart');
  return array(
    '#type' => 'ajax',
    '#commands' => array(
      ajax_command_html('#block-commerce-cart-cart .content', render($cart_block['content'])),
    ),
  );
}
 
/**
 * "Add to cart" button submit callback.
 */
function modulename_add_to_cart_form_submit($form, &$form_state){
  $form_state['rebuild'] = TRUE;
}

Код может конфликтовать с dev версией Commerce Cart Ajax, потому что модуль достаточно кривой.

Написанное актуально для
Drupal Commerce 1.x

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

Реализация AJAX кнопки &quot;Add more&quot; с помощью progressive enhancement

Пример реализации формы с кнопкой Add more, добавляющей бесконечное количество полей Name:

Создаём форму, которая будет работать с выключенным Javascript:

/**
 * Form builder.
 */
function example_add_more_form($form, &$form_state) {
  $form['names'] = array(
    '#tree' => TRUE,
  );
 
  // See example_add_more_form_add().
  if (empty($form_state['name_count'])) {
    $form_state['name_count'] = 1;
  }
 
  for ($i = 0; $i < $form_state['name_count']; $i++) {
    $form['names'][$i]['name'] = array(
      '#type' => 'textfield',
      '#title' => t('Name'),
    );
  }
 
  $form['add_more'] = array(
    '#type' => 'submit',
    '#value' => t('Add more'),
    '#submit' => array('example_add_more_form_add'),
  );
 
  return $form;
}
 
/**
 * "Add more" button submit callback.
 */
function example_add_more_form_add($form, &$form_state) {
  $form_state['name_count']++;
  $form_state['rebuild'] = TRUE;
}

Добавляем AJAX функционал:

/**
 * Form builder.
 */
function example_add_more_form($form, &$form_state) {
  $form['names'] = array(
    '#tree' => TRUE,
    '#prefix' => '<div id="names-wrapper">', // <-- New
    '#suffix' => '</div>',                   // <-- New
  );
 
  // See example_add_more_form_add().
  if (empty($form_state['name_count'])) {
    $form_state['name_count'] = 1;
  }
 
  for ($i = 0; $i < $form_state['name_count']; $i++) {
    $form['names'][$i]['name'] = array(
      '#type' => 'textfield',
      '#title' => t('Name'),
    );
  }
 
  $form['add_more'] = array(
    '#type' => 'submit',
    '#value' => t('Add more'),
    '#submit' => array('example_add_more_form_add'),
    '#ajax' => array( // <-- New
      'wrapper' => 'names-wrapper',
      'callback' => 'example_add_more_form_update',
    ),
  );
 
  return $form;
}
 
/**
 * "Add more" button submit callback.
 */
function example_add_more_form_add($form, &$form_state) {
  $form_state['name_count']++;
  $form_state['rebuild'] = TRUE;
}
 
/**
 * "Add more" button ajax callback.
 */
function example_add_more_form_update($form, $form_state) { // <-- New
  return $form['names'];
}

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

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