Respostas:
De vez em quando, ao trabalhar com o Ctools Page manager e Panels , é útil adicionar plug-ins personalizados do Ctools.
Os plug-ins do Ctools vêm em um grande número de formas e em outros módulos, como Feeds , Addressfield e Openlayers, usam o Ctools para fornecer plugins extensíveis por outros módulos. As formas mais comuns de plug-in, porém, são provavelmente "tipo de conteúdo" e "acesso". O primeiro não deve ser confundido com a entidade "conteúdo" e seus pacotes configuráveis, também chamados de tipos de conteúdo.
Primeiro, o padrão :
Para que qualquer módulo forneça plug-ins do ctools, primeiro informe ao Ctools onde procurá-los. O gancho abaixo diz que fornecemos plugins para ctools, dos tipos "content_types" e "access". A função pode ser simplificada, mas dessa maneira garantimos que apenas o módulo correto seja informado sobre os plug-ins, além de verificar se há arquivos no disco quando realmente fornecemos o tipo de plug-in solicitado.
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;
}
}
Abaixo está um exemplo de estrutura de diretório para um módulo que fornece dois plugins. Um tipo de conteúdo e um plug-in de acesso.
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
Plug-in do tipo de conteúdo
Um tipo de conteúdo no vocabulário Ctools é mais conhecido como "Painel", conforme fornecido por, por exemplo, Visualizações. Nesta pergunta: Existe uma maneira de interceptar uma lista de NIDs criados por uma exibição e usá-los como um filtro para outra exibição? , o autor pergunta sobre a programação de argumentos de argumentos para uma exibição. Embora isso não seja muito difícil, a pergunta de acompanhamento rapidamente se torna: "Como eu exibo os resultados?".
Uma resposta será criar um novo "tipo de conteúdo".
Agora, o plug-in do tipo de conteúdo real, novamente usando a pergunta Views acima, pode ficar assim:
$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;
}
Com este módulo ativado, agora deve haver uma nova categoria nos painéis, 'Minha categoria personalizada', em que um deve encontrar um único painel, renderizando o código acima.
Plug-in de acesso
O plug-in de acesso abaixo fornecerá a capacidade de arquivar variantes e / ou painéis com base na profundidade de um termo, medido a partir da raiz do vocabulário.
<?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];
}
Os plugins do CTools são pequenos arquivos que podem fazer parte de qualquer módulo como forma de ampliar sua funcionalidade. Eles podem ser usados para fornecer componentes (painéis), adicionar opções de estilos adicionais aos seus painéis, etc.
Consulte a página CTools Plugins without Panels para obter a documentação passo a passo. Então, brevemente, é assim:
Você precisa adicionar as dependências do CTools ao seu .info
arquivo como:
dependencies[] = ctools
dependencies[] = panels
Informe ao CTools onde seu plug-in está localizado:
<?php
function MYMODULE_ctools_plugin_directory($module, $plugin) {
if (($module == 'ctools') && ($plugin == 'content_types')) {
return 'plugins/content_types';
}
}
?>
Implemente o plug-in em um .inc
arquivo (por padrão, como $module.$api.inc
). Exemplo de código do plug-in:
<?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;
}
?>
A localização padrão dos plug-ins é semelhante a:
MYMODULE/
plugins/
content_types/
templates/
MYMODULE.info
MYMODULE.module
Para obter mais exemplos, verifique o ctools_plugin_example
módulo que faz parte do módulo CTools ou consulte a página de Ajuda ( Exemplos de plug-ins do CTools ) na interface do usuário do Drupal depois de ativar o módulo.
No Drupal 8, isso agora faz parte do núcleo (consulte: Drupal \ Component \ Plugin ) e fornece herança de objeto, interfaces de objeto e encapsulamento de arquivo único. Veja: Drupal 8 agora: plugins orientados a objetos no Drupal 7