Respuestas:
De vez en cuando, cuando se trabaja con el Administrador de páginas de Ctools y los Paneles , es útil agregar complementos personalizados de Ctools.
Los complementos de Ctools vienen en una gran cantidad de formas, y otros módulos, como Feeds , Addressfield y Openlayers, utilizan Ctools para proporcionar complementos extensibles por otros módulos. Sin embargo, las formas más comunes de complementos son probablemente "tipo de contenido" y "acceso". El primero no debe confundirse con la entidad "contenido" y sus paquetes, también llamados tipos de contenido.
Primero, el repetitivo :
Para que cualquier módulo proporcione complementos de ctools, primero deben decirle a Ctools dónde buscarlos. El siguiente enlace dice que proporcionamos complementos para ctools, de los tipos "content_types" y "access". La función podría simplificarse, pero de esta manera nos aseguramos de que solo el módulo correcto reciba información sobre los complementos, así como de que solo escanee el disco en busca de archivos cuando realmente proporcionemos el tipo de complemento que se solicita.
function HOOK_ctools_plugin_directory($owner, $plugin_type) {
// We'll be nice and limit scandir() calls.
if ($owner == 'ctools' && ($plugin_type == 'content_types' || $plugin_type == 'access')) {
return 'plugins/' . $plugin_type;
}
}
A continuación se muestra un ejemplo de estructura de directorios para un módulo que proporciona dos complementos. Un tipo de contenido y un complemento de acceso.
module/
module/module.info
module/module.module
module/plugins/
module/plugins/content_types/
module/plugins/content_types/two_views_in_one.inc
module/plugins/access/
module/plugins/access/term_depth.inc
Complemento de tipo de contenido
Un tipo de contenido en el vocabulario de Ctools, se conoce más a menudo como un "Panel", como lo proporcionan, por ejemplo, Vistas. En esta pregunta: ¿Hay alguna manera de interceptar una lista de NID creados por una vista y usarlos como filtro para otra vista? , el autor pregunta sobre la alimentación programática de argumentos a una vista. Si bien en sí mismo no es muy difícil, la pregunta de seguimiento se convierte rápidamente en "¿Cómo muestro los resultados?".
Una respuesta será crear un nuevo "tipo de contenido".
Ahora, el complemento de tipo de contenido real, nuevamente usando la pregunta Vistas de arriba, podría verse así:
$plugin = array(
'title' => t('Render a View with arguments from another'),
'single' => TRUE,
'category' => array(t('My custom category'), -9),
// Despite having no "settings" we need this function to pass back a form, or we'll loose the context and title settings.
'edit form' => 'module_content_type_edit_form',
'render callback' => 'module_content_type_render',
);
function module_content_type_render($subtype, $conf, $args, $context = NULL) {
$block = new stdClass;
$block->title = 'My View';
$view = views_get_view('get_nids');
$view->preview('display_machine_name', array($arg1, $arg2));
$nids = '';
foreach($view->result as $node) {
$nids += $node->nid . ',';
}
$nids = rtrim($nids, ',');
$view = views_get_view('get_related');
$view->execute_display('display_machine_name', array($nids));
$block->content = $view->render();
return $block;
}
/**
* 'Edit form' callback for the content type.
*/
function module_content_type_edit_form($form, &$form_state) {
// No settings beyond context, which has already been handled.
return $form;
}
Con este módulo habilitado, ahora debería haber una nueva categoría en Paneles, 'Mi categoría personalizada', donde en uno debería encontrar un solo panel, representando el código desde arriba.
Complemento de acceso
El complemento de acceso a continuación proporcionará la capacidad de archivar variantes y / o paneles basados en la profundidad de un término medido desde la raíz del vocabulario.
<?php
/**
* @file
* Plugin to provide access control based upon a parent term.
*/
/**
* Plugins are described by creating a $plugin array which will be used
* by the system that includes this file.
*/
$plugin = array(
'title' => t("Taxonomy: term depth"),
'description' => t('Control access by the depth of a term.'),
'callback' => 'term_depth_term_depth_ctools_access_check',
'default' => array('vid' => array(), 'depth' => 0),
'settings form' => 'term_depth_term_depth_ctools_access_settings',
'settings form validation' => 'term_depth_term_depth_ctools_access_settings_validate',
'settings form submit' => 'term_depth_term_depth_ctools_access_settings_submit',
'summary' => 'term_depth_term_depth_ctools_access_summary',
'required context' => new ctools_context_required(t('Term'), array('taxonomy_term', 'terms')),
);
/**
* Settings form for the 'term depth' access plugin.
*/
function term_depth_term_depth_ctools_access_settings($form, &$form_state, $conf) {
// If no configuration was saved before, set some defaults.
if (empty($conf)) {
$conf = array(
'vid' => 0,
);
}
if (!isset($conf['vid'])) {
$conf['vid'] = 0;
}
// Loop over each of the configured vocabularies.
foreach (taxonomy_get_vocabularies() as $vid => $vocabulary) {
$options[$vid] = $vocabulary->name;
}
$form['settings']['vid'] = array(
'#title' => t('Vocabulary'),
'#type' => 'select',
'#options' => $options,
'#description' => t('Select the vocabulary for this form. If there exists a parent term in that vocabulary, this access check will succeed.'),
'#id' => 'ctools-select-vid',
'#default_value' => $conf['vid'],
'#required' => TRUE,
);
$form['settings']['depth'] = array(
'#title' => t('Depth'),
'#type' => 'textfield',
'#description' => t('Set the required depth of the term. If the term exists at the right depth, this access check will succeed.'),
'#default_value' => $conf['depth'],
'#required' => TRUE,
);
return $form;
}
/**
* Submit function for the access plugins settings.
*
* We cast all settings to numbers to ensure they can be safely handled.
*/
function term_depth_term_depth_ctools_access_settings_submit($form, $form_state) {
foreach (array('depth', 'vid') as $key) {
$form_state['conf'][$key] = (integer) $form_state['values']['settings'][$key];
}
}
/**
* Check for access.
*/
function term_depth_term_depth_ctools_access_check($conf, $context) {
// As far as I know there should always be a context at this point, but this
// is safe.
if (empty($context) || empty($context->data) || empty($context->data->vid) || empty($context->data->tid)) {
return FALSE;
}
// Get the $vid.
if (!isset($conf['vid'])) {
return FALSE;
}
$depth = _term_depth($context->data->tid);
return ($depth == $conf['depth']);
}
/**
* Provide a summary description based upon the checked terms.
*/
function term_depth_term_depth_ctools_access_summary($conf, $context) {
$vocab = taxonomy_vocabulary_load($conf['vid']);
return t('"@term" has parent in vocabulary "@vocab" at @depth', array(
'@term' => $context->identifier,
'@vocab' => $vocab->name,
'@depth' => $conf['depth'],
));
}
/**
* Find the depth of a term.
*/
function _term_depth($tid) {
static $depths = array();
if (!isset($depths[$tid])) {
$parent = db_select('taxonomy_term_hierarchy', 'th')
->fields('th', array('parent'))
->condition('tid', $tid)
->execute()->fetchField();
if ($parent == 0) {
$depths[$tid] = 1;
}
else {
$depths[$tid] = 1 + _term_depth($parent);
}
}
return $depths[$tid];
}
Los complementos de CTools son pequeños archivos que pueden formar parte de cualquier módulo como forma de extender su funcionalidad. Se pueden usar para proporcionar componentes (paneles), agregar opciones de estilos adicionales a sus paneles, etc.
Consulte la página Complementos de CTools sin paneles para obtener documentación paso a paso. Entonces brevemente dice:
Necesita agregar dependencias CTools en su .info
archivo como:
dependencies[] = ctools
dependencies[] = panels
Dígale a CTools dónde se encuentra su complemento:
<?php
function MYMODULE_ctools_plugin_directory($module, $plugin) {
if (($module == 'ctools') && ($plugin == 'content_types')) {
return 'plugins/content_types';
}
}
?>
Implemente el complemento en un .inc
archivo (de forma predeterminada como $module.$api.inc
). Código de complemento de ejemplo:
<?php
$plugin = array(
'title' => t('Twitter feed'),
'description' => t('Twitter feed'),
'category' => 'Widgets',
'icon' => '',
'render callback' => 'twitter_block',
'defaults' => array(),
);
// render callback
function twitter_block() {
// Add twitter widget javascript
$url = TWITTER_USER
$widget_id = TWITTER_WIDGET_ID;
$data = array();
$data['url'] = $url;
$data['widget_id'] = $widget_id;
$content = array(
'#theme' => 'my_block',
'#content' => $data,
);
$block = new stdClass();
$block->content = $content;
$block->title = '';
$block->id = 'twitter_block';
return $block;
}
?>
La ubicación predeterminada de los complementos se ve así:
MYMODULE/
plugins/
content_types/
templates/
MYMODULE.info
MYMODULE.module
Para obtener más ejemplos, compruebe el ctools_plugin_example
módulo que forma parte del módulo CTools o consulte la página de Ayuda ( Ejemplos de complementos CTools ) en la interfaz de usuario de Drupal después de habilitar el módulo.
En Drupal 8, esto ahora es parte del núcleo (ver: Drupal \ Component \ Plugin ) y proporciona herencia de objetos, interfaces de objetos y encapsulación de archivos individuales. Ver: Drupal 8 ahora: complementos orientados a objetos en Drupal 7