Hoe maak je een "page manager" plugin

Posted by: 
Jonathan

Gast post door Jonathan Ramael:

Voor mijn vorig project, was een lijst met merken (taxonomie) die doorlinkten naar producten een vereiste voor de klant. Op deze producten pagina had je

nog een overzicht met producten van hetzelfde type en merk (bv. Horloges van het merk Rado). Kort samengevat, de lijst met taxonomie termen wordt gebruikt

als navigatie naar producten. Omdat de termen dus niet linken naar de merken maar de producten was er nood aan wat zelfgeschreven code.

De klant had geen specifieke voorkeur naar welk product van het merk de link moest verwijzen dus hebben we gekozen voor het laatst toegevoegde product.

De productenpagina is aangemaakt met de page manager aangezien we een andere layout gebruiken per product type. Dat is waar de page panager plugin aan te

pas komt.

Ik neem je mee doorheen het proces:

We voegen de plugin toe aan onze eigen glue module, deze is geplaatst onder sites/all/modules/custom, daar bevindt zich onze persoonlijke glue_site map.

Als eerste geven we aan ctools door waar onze plugin zich bevindt, we doen dit in glue_site.module in onze glue_site map.

//glue_site.module

/**
 * Implements hook_ctools_plugin_directory()
 * 
 * @param type $owner
 * @param type $plugin_type
 * @return type
 */
function glue_site_ctools_plugin_directory($owner, $plugin_type) {
  // We'll be nice and limit scandir() calls.
  if ($owner == 'ctools' && ($plugin_type == 'content_types' )) {
    return 'plugins/' . $plugin_type;
  }
}

Deze functie toont aan dat we een plugin maken voor ctool van het type 'content_type' ('inhouds_type').
Op deze manier zorgen we ervoor dat alleen de juiste module de informatie doorkrijgt van de plugin en weet waar ze te vinden is. Dit verzekert ook dat het

op de schijf zoekt naar bestanden waarbij het correcte content type is meegegeven. Een content type in ctools is een pane, zoals meegegeven bij views

bijvoorbeeld.

De rest van onze code gaat in een bestand genaamd glue_site.products.inc, te vinden in de content_types map.

De huidige bestandsstructuur zier er uit als volgt:

modules/custom/glue_site/
modules/custom/glue_site/glue_site.info
modules/custom/glue_site/glue_site.module
modules/custom/glue_site/plugins/
modules/custom/glue_site/plugins/content_types/
modules/custom/glue_site/plugins/content_types/glue_site,products,inc

Tot zover was het niet echt ingewikkeld nu de rest van onze plugin:

Eerst definiëren we de plugin:

//glue_site.products.inc

$plugin = array(
  'title' => t('Products block'),
  'single' => TRUE,
  'category' => array(t('Mulders custom'), -9),
  'edit form' => 'glue_site_content_type_products_edit_form',
  'render callback' => 'glue_site_content_type_products_render',
  // The default context.
  'defaults' => array('rendering' => ''),
  'all contexts' => TRUE,
);

Zoals je kan zien is het een simpele array, die de titel van het blok genereerd, single wordt op "waar" (TRUE) gezet wanneer we geen subtypes gebruiken,

de categorie waar het te vinden is in de page manager interface, de "edit form functie" zoals hieronder beschreven, de eigen functie daaronder en de

standaard ('rendering' is een aangepast/eigen veld in het instellingen formulier in de interface, zie hieronder.

page manager plugin form

We gebruiken aangepaste veld 'rendering' om onze plugin door te geven hoe we onze output willen weergeven, allemaal vanuit de page manager interface.

page manager plugin context form

We geven ook een context door aan de page manager, om zo te definiëren naar welke term het product moet refereren.

page manager panes

Nota: Dit blok (eigenlijk een pane) zal enkel beschikbaar zijn via de page manager, niet bij de "blokken" in drupal.

Eens we dit hebben gedaan, gaan we verder met onze functie:

//glue_site.products.inc

function glue_site_content_type_products_render($subtype, $conf, $args, $context) {

  $block = new stdClass;
  $block->title = t('Brand links to product');

  $product_type_tid = $context['context_entity:taxonomy_term_1']->data->tid;
  $product_type_name = $conf['rendering'];

  $block->content = glue_site_create_block_content($product_type_tid, $product_type_name);

  return $block;
}

$subtype: in this case it's glue_site.products, it's needed for ctools to find your plugin
$conf: contains the input from the fields in the block ui
$args: empty but it's still needed, otherwise the function won't do it's job
$context: contains the contexts we set on the page manager ui

Daarna declareren we $block als een stdClass en geven we het een titel, daarna declareren we de variabelen die we nodig hebben in onze volgende functie.
We halen $product_type_tid uit de context, hiervoor kan je dsm($context); gebruiken om te vinden wat je nodig hebt, daarna halen we $product_type_name uit

het configuratie veld in de pane ui.

dsm example

Dit is een voorbeeld van het gebruik van dsm($context); om te zien wat er exact in zit.

Next:

/**
 * Callback to construct block
 * 
 * @param type $product_type_tid
 * @param type $product_type
 * @return string
 */
function glue_site_create_block_content($product_type_tid, $product_type) {


  global $language;//current language
  $vid = 2; //brands vocabulary id
  $terms = taxonomy_get_tree($vid);//get all terms 
  $termlist = array(); //array will be populated with the links we create
  $data = '';//the output

  foreach ($terms as $term) {

    $query = new EntityFieldQuery;

    $result = $query
        ->entityCondition('entity_type', 'node')
        ->propertyCondition('status', 1)//the node is published
        ->propertyOrderBy('created', 'desc')//we want the newest first
        ->range(0, 1)//we only need the first one returned
        ->fieldCondition('field_product_producttype', 'tid', $product_type_tid, '=')//the referenced term 'uurwerken'
        ->fieldCondition('field_product_brand', 'tid', $term->tid, '=')//the current brand term id
        ->propertyCondition('language', $language->language, '=')//only get nodes in the current language
        ->execute();

    if ($result) {

      //create the link to the products returned from the query
      if ($product_type == 'uurwerken' ) {

        foreach ($result['node'] as $node) {

          $termlist[] = l($term->name, drupal_lookup_path('alias', "node/" . $node->nid));
//I fetched the alias to build the link to the product 

        }//end foreach $result['node'] as $node
      }
      elseif ($product_type == 'juwelen') {
	//some other logic
      }
    }//end if
    
    
  }//end foreach $terms as $term

  if ($product_type == 'uurwerken') {
    $data = glue_site_render_watches($termlist);
  }
  elseif ($product_type == 'juwelen') {
    $data = glue_site_render_jewels($termlist);
  }


  return $data;
}

De commentaar verklaart wat EntityFieldQuery doet.

Het laatste van de functie is wat standaard php code om standaard te doen wat je wilt zodra je de resultaten hebt van de query.

De volgende functie rendert onze links als een lijst.

function glue_site_render_watches($termlist) {


  $vars['items'] = $termlist;
  $vars['title'] = null;
  $vars['type'] = "ul";
  $vars['attributes'] = array('id' => 'taxonomylist');

  $data = theme_item_list($vars);//drupal api

  return $data;
}

function glue_site_render_jewels($termlist) {

  //some magical code doing anything you want

}

We zijn er bijna, nog 2 kleine functies, maar wel zeer belangrijk om het formulier te laten werken in de page manager ui:

Met deze functie maken we het formulier, dit kan vrij basis, maar ook zeer krachtig, het laat de gebruiker toe om de verplichtte waarden het formulier in te vullen in plaats van dit in de code te moeten meegeven.

/**
 * 'Edit form' callback for the content type.
 */
function glue_site_content_type_products_edit_form($form, &$form_state) {
  // No settings beyond context, which has already been handled.
  $conf = $form_state['conf'];

  $form['rendering'] = array(
    '#type' => 'textfield',
    '#title' => t('rendering'),
    '#default_value' => !empty($conf['rendering']) ? $conf['rendering'] : '',
    '#description' => t('Fill in rendering type juwelen or uurwerken'),
  );

  return $form;
}

Dit genereert 1 tekstveld in het formulier, je kan er zoveel toevoegen als je wilt.

Deze laatste functie is de submit handler van het formulier, en bewaart de ingegeven waarden ervan:

/**
 * Submit function, note anything in the formstate[conf] automatically gets saved
 * Notice, the magic that automatically does that for you.
 */
function glue_site_content_type_products_edit_form_submit(&$form, &$form_state) {

  foreach (array_keys($form_state['plugin']['defaults']) as $key) {
    if (isset($form_state['values'][$key])) {
      $form_state['conf'][$key] = $form_state['values'][$key];
    }
  }
}

Dat was het, spendeer wat tijd om het allemaal onder de knie te krijgen. Het is niet zo moeilijk zodra je er mee weg bent en het is zeer krachtig om bepaalde zaken te verwezelijken.

Have fun!

Reactie toevoegen