Модуль для добавления нового способа отображения в Display Fields для модуля TableField

Пишу модуль для Drupal 6Пишу модуль для добавления нового способа отображения (для настройки в Display Fields) для модуля TableField без вмешательства в его исходный код.

Цель написания материала: запомнить для себя на будущее и не потерять, получение конструктивной критики, помощь народу, который вдруг сюда зайдёт в поисках похожего решения. Возможно ещё хочу тупо выпендриться :)

Исходные данные

  1. Есть модуль TableField
  2. Drupal 6
  3. Нужно сделать для некоторых из табличных полей свой способ отображения на сайте, который заключается в наличии гламурной кнопочки "покажи мне ещё", при нажатии на которую будет выводится дополнительная строка таблички с каким-то более подробным описанием чего-либо с применением JavaScript.

Приступаю к написанию модуля

Первым делом качаю самую свежую версию и смотрю как отреагировали разработчики на мою попытку запостить баг при работе с модулем Content Complete (кто не использует этот модуль смело забиваем на окончание этого абзаца).
Естественно не отреагировали :( Поэтому я беру свой код из баг репорта и втягиваю к себе, предварительно убедившись, что Content complete всё так-же корректно не работает. Попутно завожу себе напоминалку при апдэйте проверять модуль на фикс данного бага.

Анализ задачи и способа её решния, постановка себя на рельсы

Лёгкое гугление и курение исходников по адресу sites/all/modules/tablefield, показало, что нужно дописать свой хук hook_field_formatter_info, который подсунет допольнительный formatter для вывода полей типа tablefield и повесит на него свой обработчик.

Для хранения дополнительных данных для поля more info решаю использовать последнюю ячейку в каждой строке. Если она пустая, то кнопка "Покажи ещё" не выводится, если не пустая, то выводится во второй ячейке слева (так надо по дизайну).

В обработчике нужно будет переколбасить данные для таблицы так, что-бы они замечательно зашли в функцию theme('table', blah-blah-blah), которую в конечном итоге использует модуль tablefield для вывода таблицы.

Следом иду в admin/build/modules/list и придумываю своему модулю кошерное имя. Мой модуль будет называться tablefield_more и относиться к CCK.

Реализация

Создаю рыбу модуля для Drupal:

  1. Создаю папку для своего модуля - tablefield_more
  2. Создаю файл с описанием модуля - tablefield_more.info
  3. Создаю пустой файл, где будет происходить всё таинство: tablefield_more.module

Пишу собственно модуль:

1. Добиваюсь появления нужно мне форматтера с работающим обработчиком:

Пишу хуки hook_theme, hook_field_formatter_info и рыбу функции темизации вывода.

Включаю свой модуль в admin/build/modules/list, включаю для нужного мне tablefield в admin/content/node-type/[content-type]/display "Tabular View with More info button" вместо "Tabular View"

Проверяю, чтобы на странице материала появился дебаг и тихо радуюсь результату :)

Вот минимальный код модуля, который делает вышеописанное:

<?php
/**
 * @file
 * This module provides a additional formatter
 * for tablefield module
 */

/**
 * Implementation of hook_theme().
 */
function tablefield_more_theme() {
  return array(
    'tablefield_more_formatter_more' => array(
      'arguments' => array(
        'element' => NULL,
      ),
    ),
  );
}

/**
 * Implementation of hook_field_formatter_info().
 */
function tablefield_more_field_formatter_info() {
  return array(
    'more' => array(
      'label' => t('Tabular View with More info button'),
      'multiple values' => CONTENT_HANDLE_CORE,
      'field types' => array('tablefield'),
    ),
  );
}

/**
 * Theme function for more table display
 */
function theme_tablefield_more_formatter_more($element) {
  echo '<pre>';
  print_r($element['#item']);
  echo '</pre>';
  return $element['#item']['value'];
}

 

2. Усугубляю модуль до нужного функционала:

Как выяснилось в процессе написания, модуль tablefield херит данные в обрабатываемом виде и подменяет их темизованными в функции hook_field на этапе sanitize. И, т.к. мне не удалось в разумные сроки влезть в данные до этого этапа, пришлось применить небольшие грабельки в виде node_load() в форматере поля.

Следом добавляю немножно JavaScript для разворачивания/сворачивания строки с дополнительной информацией. Поскольку на моём сайте активно используется jQuery и он живёт в Core D6, то колбашу фичу под jQuery.

На выходе получилась вот такая конструкция:

tablefield_more.info

name = TableField More
description = Add Formatter with More info button for TableField
dependencies[] = content
dependencies[] = tablefield
package = CCK
core = 6.x
version = "6.x-2.1"

tablefield_more.css

.tablefield span.more_info{
    display:block;
    width:12px;
    height:12px;
    background:url(./more_info_bg.gif) no-repeat top left;
    cursor:pointer;
}
.tablefield span.selected{
    background-position:top right;
}
.tablefield tr.more_info{
    display:none;
}

tablefield_more.js

$(document).ready(function(){
    $('.tablefield span.more_info').toggle(
        function() {
            $(this).addClass("selected");
            $(this).parent().parent().next().css('display','table-row');
        },
        function() {
            $(this).removeClass("selected");
            $(this).parent().parent().next().css('display','none');
        }
    );
});

tablefield_more.module

<?php
/**
 * @file
 * This module provides a additional formatter 
 * for tablefield module
 */

/**
 * Implementation of hook_theme().
 */
function tablefield_more_theme() {
  return array(
    'tablefield_more_formatter_more' => array(
      'arguments' => array(
        'element' => NULL,
      ),
    ),
  );
}
/**
 * Implementation of hook_field_formatter_info().
 */
function tablefield_more_field_formatter_info() {
  return array(
    'more' => array(
      'label' => t('Tabular View with More info button'), 
      'multiple values' => CONTENT_HANDLE_CORE, 
      'field types' => array('tablefield'),
    ),
  ); 
}

/**
 * Theme function for more table display 
 */
function theme_tablefield_more_formatter_more($element) {
  $delta=$element['#item']['#delta'];
  $format=$element['#item']['format'];
  if(isset($element['#item']['tabledata'])){
    $tabledata=$element['#item']['tabledata'];
    $field=$element['#node']->$element['#field_name'];
  }else{
    $node=node_load($element['#node']->nid);
    $field=$node->$element['#field_name'];
    $tabledata=unserialize($field[$delta]['value']);
    $tabledata=tablefield_rationalize_table($tabledata);
    unset($node);
  }
  if (isset($tabledata)) {
    $table=array();
    $addon_rows=0;
    if (!empty($tabledata)) {
      drupal_add_css(drupal_get_path('module', 'tablefield_more') . '/tablefield_more.css','module','screen');
      drupal_add_js(drupal_get_path('module', 'tablefield_more') . '/tablefield_more.js','theme');
      $last_cell=count($tabledata[0])-1;
      $add_rows=0;
      foreach ($tabledata as $row_key => $row) {
       $next_row_data='';
         $add_cells=0;
       if(!empty($row[$last_cell])){
        $next_row_data=$row[$last_cell];
       }
       foreach ($row as $col_key => $cell) {
        if ($col_key<$last_cell){        //ignore last cell
         if (!empty($field['cell_processing'])) {
           $table[$row_key+$add_rows][$col_key+$add_cells] = array(
            'data' => check_markup($cell, $format),
            'class' => 'row-' . ($row_key + $add_rows) . ' col-' . ($col_key + $add_cells)
           );
         }else{
           $table[$row_key+$add_rows][$col_key+$add_cells] = array(
            'data' => check_plain($cell),
            'class' => 'row-' . ($row_key + $add_rows) . ' col-' . ($col_key + $add_cells)
           );
          }
         if($col_key==0){    //first cell processed, adding more info cell
          $add_cells++;
          $table[$row_key+$add_rows][$col_key+$add_cells] = array(
           'data' => (empty($next_row_data)?'':'<span class="more_info"></span>'),
           'class' => 'more_info row-' . ($row_key + $add_rows) . ' col-' . ($col_key + $add_cells)
          );
         }
        }
       }
       if(!empty($next_row_data)){    //adding more info row
        $add_rows++;
         $table[$row_key+$add_rows]=array('data'=>array(),'class'=>'more_info');
        if (!empty($field['cell_processing'])) {
         $table[$row_key+$add_rows]['data'][0] = array(
          'data' => check_markup($next_row_data, $format),
          'class' => 'row-' . ($row_key + $add_rows) . ' col-0',
          'colspan' => ($last_cell+1)
         );
        }else{
         $table[$row_key+$add_rows]['data'][0] = array(
          'data' => check_plain($next_row_data),
          'class' => 'row-' . ($row_key + $add_rows) . ' col-0',
          'colspan' => ($last_cell+1)
         );
        }
       }
      }
    }
    $element['#item']['value'] = theme('tablefield_view', array(), $table, $element['#field_name'], $delta);
  }
  return $element['#item']['value'];
}

Плюс more_info_bg.gif - картинка размером 24x12px, где слева иконка для разворачивания поля (12x12px) и справа иконка для сворачивания (12х12px)

Скачать модуль tablefield_more (бесплатно, бетта первой версии).

Итого

Пример сделать очень красивым не получилось - пока окончательно не ясно как работают некоторые части друпала. Но общее понятие, как я это сделал, для применения в будущих проектах, пожалуй, создалось. Совсем не плохо было-бы сделать без node_load() в форматере, но это доделаю когда позволит экспиренс или комментарий в тему.

Жду отзывов! Если с вашей точки зрения получился полный отстой, то просьба уточнять что именно не так - критика приветствуется и исправления принимаются.

 

RSSAdd to Google

Обсуждение:

Как вам материальчик? *



















Тема:

Мысль:

Как звать-то:

Почта:

PS: Комментарии публикуются только после проверки модератором.

PPS: E-mail'ы не публикуются.

* обязательные поля