Drupal para 

desenvolvedores


    

introdução


Neste curso alunos aprenderão sobre :

  • Ferramentas e recursos disponíveis para desenvolvedores,;
  • Anatomia de módulos do Drupal
  • Implementação dos hooks mais comuns do Drupal
  • Interação com o sistema de Menu do Drupal
  • formulários no Drupal
  • Abstração de Banco de Dados com Drupal

ferramentas

o modulo devel


Conjunto de módulos que provém funções para
  •  debugging
  •  geração de conteúdo de testes
  •  análise de queries 

Instalação

  • Baixar: drush dl devel
  •  drush en devel

debugging de entidades


Depois de instalado, o Devel fornece uma aba, "Devel" na página administrativa da entidade.

Blocos do devel


  • Development block
    • Lista de links para páginas e funcionalides como:
      • Configurações
      • Documentação
      • Debugging de objetos
      • Limpar o Cache
      • etc...
  • Switch User Block
    • Troca de usuários com dois clicks
  • Execute PHP
    • Textarea para executar php no backend

Funções para debug


  • dsm($var, $label)
    • Mostra qualquer variável em krumo na interface
    • coloca o krumo na area de mensagens
    • **nao funciona bem na camada de theme
  • kpr($var)
    • Mostra qualquer variável em krumo na interface
    • coloca o krumo no topo do body
  • dd($var, $label)
    • Mostra qualquer variável em formato de linha de comando
    •  Salva debug no arquivo drupal_debug.txt dentro da file tmp dir configurada no drupal

query logging


Mostra queries feitas no banco de dados por request.

  • Completo com tempo de query
  • Função que executou a query
  • Ordenação
  • Configuração
    • /config/development/devel
    • Display query logger

tracing erros


  • ddebug_backtrace
  • Settings
    • /config/development/devel
    • Error handlers: Krumo backtrace

Demais ferramentas do devel


  • Reconstrução do  theme registry em toda página
    • Para que novas implementações de hooks de temas entrem em efeito sem limpar manualmente
  • Utilização de memória
    • Mostra quanta memória foi utilizada para construir a página
    • Setting: Display memory usage
  • Tempo de execução
    • Mostra quanto tempo levou para executar a página
  • Geração de COnteúdo para Testes
    • Devel Generate, Config -> Development -> Generate Content

documentação da api do drupal


Documentação de todo o código disponível online, com uma busca excelente, incluindo tutoriais e explicação conceituais sobre os vários sistemas do Drupal.


http://api.drupal.org

Drupal Reference API


Por função, está disponível:

  • Versão

  • Parametros

  • Retorno

  • Snippets

  • Referencias

tutoriais e discussões


  • Tutorias

  • Discussões

    • Grupos de usuários e desenvolvedores focados em diferentes areas e aspectos de sites contribuindo conhecimento.

  • Documentação de módulo

a anatomia de um módulo do drupal



Um módulo é um mecanismo que permite extender a funcionalidade padrão do drupal.

tipos de módulos


  • Core

    • Block, Node, User

  • Contrib

    • Views, Pathauto, Media

  • Custom

    • Seu próprio modulo aqui! :)

drupal framework


  • Sistema de Hooks

    • Eventos

    • Modificações

  • Drupal API

    • Form API

    • Database API

    • File API

    • Fields API

    • Schema API

  • Sistema de temas

diretórios de módulos


O primeiro passo é colocar esse módulo no lugar certo. Esse lugar é sempre dentro da pasta:

  • sites/all/modules


Por padrão sempre criamos 2 pastas dentro da pasta modules:

  • sites/all/modules/contrib para os módulos baixados do drupal.org

  • sites/all/modules/custom para os nossos

arquivos obrigatórios


O segundo passo na criação de um módulo é a criação de 2 arquivos

  • .info: contém informações básicas como nome do módulo, descrição, versão e dependencias

    https://drupal.org/node/542202

  • .module: o arquivo base para criação do código e implementação dos hooks

Vamos criar nosso modulo


Ele não fara nada ainda. Habilite.

o sistema de hooks de eventos


Hooks

Permitem a um módulo interagir com o drupal core. Para isso só é preciso implementar uma função com um nome padrão, por exemplo:

  • hook_node_insert

  • meumodulo_node_insert

tipos de hooks


Existem básicamente 2 tipos de hooks:

  • Eventos: São chamados em determinados eventos do drupal, como login de usuário, inserção de conteúdo ou edição de um usuário

  • Alteração: São chamados para modificar algum comportamento padrão do drupal, como modificar um formulário ou um item de menu.

    meumodule_form_alter(&$form, &$form_state)

exemplo


Mostrando uma mensagem após o login:


meumodulo_user_login(&$edit, $account) {

  drupal_set_message(t('Olá, @name', array('@name' => $account->name)));

}

dicas de código seguro


Segurança em sistemas web é importante devído ao largo acesso ao sistema pela internet.

O Drupal fornece várias ferramentas para garantir segurança a nível de código.

vulnerABILIDADES


  • XSS - Cross-site scripting
    • Execução de código não autorizado
  • XSFR - Fraude de request cross-site 
    • Execução remota para usuários logrados
  • SQL Injection
    • Variáveis maléficas inseridas em queries do banco de dados

como o drupal lida com input


  • Dados são salvos em sua forma cru no DB.
  • Dados são sanitizados na saída
    • Formatos de texto
  • A responsabilidade é dos desenvolvedores
  • Drupal fornece ferramentas que você deve utilizar

formatos de texto


  • admin/config/content/ formats 
  • Filtros de Plain Text
    Exibe qualquer HTML como texto limpo

    <div class=”blue”>Hello!</div> torna

    &lt;div class="blue"&gt;Hello!&lt;/ div&gt; 

formatos de texto


  • /admin/config/content/ formats
  • Filtered HTML Filters
    • Limite as HTML tags permitidas

<a> <em> <strong> <cite> <blockquote> <code> <ul> <ol> <li> <dl> <dt> <dd>


    • Eventos e URLs de JavaScript e CSS sáo sempre retirados
      • <div class=”blue”>Hello!</div> vira Hello!
      • <code class="blue" style="border:1px;">Hello!</code> vira <code class=”blue”>Hello!</code> 

formatos de texto


  • /admin/config/content/ formats
  • Full HTML Filters 

como lidar com output


  • A responsabilidade é do desenvolvedor
  • O drupal te da as ferramentas, você deve utiliza-las

sanitize o output


http://api.drupal.org/api/drupal/includes--

common.inc/group/sanitization/7 page8image2544

  • check_plain() 
  • check_markup() 
  • check_url() & l()
  • t()
  • filter_xss() 
  • filter_xss_admin() 

check_plain($text)


  • Deve ser utilizado quando inserindo texto puro dentro do HTML. Nada de HTML passa.
  • Utiliza caractéres especiais ao invez de HTML. <a href='test'>Test</a> vira &lt;a href=&#039;test&#039;&gt;Test&lt;/a&gt; 

check_markup( $text, $format_id = 

NULL, $langcode = '', $cache = FALSE)


  • Deve ser utilizado quando iserindo texto rico (rich text, HTML) em HTML.
  • Permite que voce especifique o ID do formato que corresponde ao "formato de texto" que voce quer utilizar.
  • Padrão utilizado é aquele configurado no sistema.
  • Ex.: $newtext = check_markup($text, ‘filtered_html’);

check_url($uri) e l($text, $path, array $options = array())


  • check_url() retira protocolos inseguros (e.g. 
'javascript:') de uma URI e codifica para saída de valor de atributo HTML.

  • Permite 'ftp', 'http', 'https', 'irc', 'mailto', 'news', 

'nntp', 'rtsp', 'sftp', 'ssh', 'tel', 'telnet', 'webcal'

  • l() contrói um link HTML completo utilizando a função url() para o atributo href e também sanitiza o título do link.

t($string, $args = array(), $options = array())

  • Permite tradução de strings. 
  • Deve-se utilizar substituição  e variáveis substituição  para QUALQUER variável.
  • $text = t("@name's blog", array('@name' => format_username($account)));
    • !variable: Inserida como esta. Utilize essa função SOMENTE para texto já sanitizado.
    • @variable: Saída para HTML urilizando check_plain(). Utilize para qualquer coisa a ser exibida na pagina. 
    • %variable: Saída como placeholder para conteúdo submetido por usuários utilizando drupal_placeholder(), exibindo como texto em ênfase  <em>emphasized</em> .

filter_xss($string, $allowed_tags = array('a', 'em', 'strong', 'cite', 'blockquote', 'code', 'ul', 'ol', 'li', 'dl', 'dt', 'dd'))

  • Filtra string HTML para previnir vulnerabilidades de cross-site-scripting (XSS) Filters an HTML string to prevent cross-site-scripting (XSS).
  • Remove caracteres e estruturas que exploram vulnerabilidades de browsers.
  • Certifica que 
    • todos elementos HTML estão bem formados.
    • todas tags e atributos HTML estão bem formados.
    • Nenhuma tag HTML contem URLs de protocolos perigosos (e.g. javascript:).

filter_xss_admin($String)


  • Filtro bastante permissivo em relação a XSS/HTML e deve ser utilizado somente em interfaces administrativas.
  • Utilize somente para campos quais a utilização do sistema de filtragem completo não é pratica, entretanto, onde algum markup é necessário (portanto, a utilização de check_plain não é aceitável)
  • Permite que todas tags sejam utilizadas dentro do body do HTML. Guarde para scripts e styles.

enviando Email 


  • drupal_mail(
$module, $key, $to, $language, $params = array(), $from  = NULL, $send = TRUE);

  • Envio de email funciona com a definição de um template (assunto, texto, e possivelmente headers de email) e valores para substituição nos locais corretos no template.
    • Não permite a injeção de Headers
      • Usuários não conseguem inserir um Bcc no assunto

Drupal Forms


  • A Forms API (FAPI) do Drupal protege contra  XSRF utilizando um sistema de sessão e token que valida os dados do POST.

SQL Injection


  • O query builder, qual possui substituição de variáveis, utiliza a Database API para lidar com os dados de forma segura. 
  • db_merge('example') ->key(array('name' => $name))             ->fields(array( 'field1' => $value1,  'field2' => $value2,))     ->execute();

limpe todo dado fornecido por usuarios na saida!!!

  • Não confie nas pessoas.
  • Utilize as ferramentas que o Drupal lhe fornece para proteger seu site contra vulnerabilidades
  • documentação: http://drupal.org/writing-secure-code


o sistema de menu do drupal


  • Client faz um request
  • Server chama o index.php
    • Faz o bootstrap
    • Conecta com o banco
    • Instancia o usuario
  • Pede a URLs para os módulos
  • Qualquer modulo pode registrar um caminho

hook_menu

/**
 * Implements hook_menu().
 */
function meumodulo_menu() {
  $items = array();

  $items['sobre'] = array(
    'title' => 'Sobre',
    'page callback' => 'meumodulo_basic',
    'access callback' => 'user_access'
    'access arguments' => array('access content'),
  );
return $items;
}

roteamento


O drupal compara o caminho requisitado (URI) e decide qual é o match mais especifico. 

  • O caminho é um alias?
  • Qual é o módulo responsável pelo caminho
    • Que função eu chamo?
  • O usuário tem acesso?

o callback


  • Retorna a resposta para o Drupal 
    • Se voce nao retorna algo, o Drupal assume 404
  • O Drupal pega isso e envia para a camada de Tema
  • Então o Drupal devolve para o Client.

vamos criar nossa pagina


Uma pagina basica e simples. 
https://api.drupal.org/api/drupal/modules%21system%21system.api.php/function/hook_menu/7

  • Item de menu
    • title
    • callback
    • access
  • No callback
    • Elemento simples

utilizando argumentos de url em 

page callbacks 


  • Variáveis em URLs
  • Callback em arquivo separado
  • Tipos de Itens de menu

VARIÁVEIS em urls - argumentos



  $items['magic/%'] = array(
    'title' => 'Even more magical',
    'page callback' => 'menu_magic_extra',
    'page arguments' => array(1),
    'access arguments' => array('access content'),
  );


VARIÁVEIS EM URLS - callback


function menu_magic_extra($wildcard) {
  $content = array(
    '#type' => 'markup',
    '#markup' => '<p>'. t('The wildcard contains the value "%wildcard".', array('%wildcard' => $wildcard)) . '</p>',
  );
  return $content;
}

callbacks em arquivo separado


Ajuda na organização e performance.

 $items['magic/%'] = array(
    'title' => 'Even more magical',
    'page callback' => 'menu_magic_extra',
    'page arguments' => array(1),
    'access arguments' => array('access content'),
    'file' => 'menu_magic.extra.inc',
  );

tipos de itens de menu


criando uma tab


$items['user/%/magic'] = array(
    'title' => 'Magic',
    'description' => 'Magical magic for users',
    'page callback' => 'menu_magic_user_tab',
    'page arguments' => array(1),
    'access callback' => 'user_access',
    'access arguments' => array('administer users'),
    'file' => 'menu_magic.user.inc',
    'type' => MENU_LOCAL_TASK,
  );

links de menu contextuais e autoloaders


Vamos aprender a:

  • Adicionar links em widget contextuais para elementos nas paginas
  • Carregar objetos através de argumentos de URL

Links contextuais

  • Crie um novo node 
    • promote to front page
  • Novo item de menu
    • // node_load(%)
      •   $items['node/%node/magic'] = array(
      •     'title' => 'Magic',
      •     'description' => 'Do amazing and magical things',
      •     'page callback' => 'menu_magic_node_context',
      •     'page arguments' => array(1),
      •     'access arguments' => array('access content'),
      •     'file' => 'menu_magic.context.inc',
      •     'type' => MENU_LOCAL_TASK,
      •     'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE,
      •   );

Autoloaders


Dado o nome de máquina de um elemento, o Menu API pega argumento dado pela URL e carrega o objeto:
$items['node/%node/magic'] = array(

function menu_magic_node_context($node) {
  $text = $node->body['und'][0]['value'];
  $output = array(
    '#type' => 'markup',
    '#markup' => $text,
  );
  return $output;
}

utilizando o render api


  • Estrutura de dados recursiva (n-dimensional array) que representa elementos a serem apresentado
    • # representa um atributo do element
    • Sem o #, define um filho do elemento, que por sua vez  é um elemento também
  • Documentação
    • https://drupal.org/node/930760
  • Form elements 
    • http://tinyurl.com/n7d9arj
  • Theme function como elementos

render element - simples

Esse é um element bem simples, aparecerá na página, entretanto outro módulos não conseguiram sobre-escrevelos.

  $content['raw_markup'] = array(
    '#type' => 'markup',
    '#markup' => 'Truly, this is magical!',
    '#prefix' => '<p>',
    '#suffix' => '</p>',
  );

Render element com theme function

  $variables = array(
    'path' => 'http://placekitten.com/440/400',
    'alt' => t('This is a magical kitten'),
    'title' => t('This is the title'),
  );
  $content['themed_data'] = array(
    '#type' => 'markup',
    '#markup' => theme('image', $variables),
    '#prefix' => '<div class="menu-magic-image">',
    '#suffix' => '</div>',
    ),
  );

attachments em render elements


  • Possibilita que recursos como CSS e JS sejam carregados com os elementos que necessitam dos mesmos.
  • Atributo #attached
'#attached' => array(
      'css' => array(
        drupal_get_path('module', 'menu_magic') . '/menu_magic.css',
      ),
    ),

cache de render elements


Faz com que o Drupal cacheie o elemento e pega o HTML cacheado depois de construi-lo pela primeira vez. 

  • Atributo #cache
'#cache' => array(
      'keys' => array('menu_magic', 'renderable_element'), 
      'bin' => 'cache', //tabela de cache
      'expire' => time() + 30, //em segundos
      'granularity' => DRUPAL_CACHE_PER_PAGE,
    ),

alterando o page array


O Page Array representa a página sendo apresentada em forma de um render element.

hook_page_alter(&$page)

/**
 * Implements hook_page_alter().
 */
function menu_mangler_page_alter(&$page) {
  if (arg(0) == 'magic') {
    dsm($page);
    $page['content']['system_main']['renderable_element']['#type'] = 'ol';
  }
}

integrando com o theme system



Aqui aprenderemos a integrar com o sistema de Tema do Drupal, criando um novo item de tema com hook_theme(), e então criar uma função que será responsável por produzir o output. Assim como funções podem produzir output, podemos também optar por templates.

Novo modulo - spyglass

Criaremos um novo modulo para demonstração.
/**
 * Implements hook_menu().
 */
function spy_glass_menu() {
  $items = array();
  $items['node/%node/spy_glass'] = array(
    'title' => 'Spy Glass',
    'description' => 'Take a closer look',
    'page callback' => 'spy_glass_page',
    'page arguments' => array(1),
    'access callback' => 'node_access',
    'access arguments' => array('view', 1),
    'type' => MENU_LOCAL_TASK,
  );
  return $items;
}

modulo spyglass - helper

/**
 * Collecting some super secret information about this node.
 */
function _spy_glass_serious_spying_business($node) {
  $information = array();
  $information[] = array(
    'title' => t('Title Character Count'),
    'data' => t('There are @num characters in the $node->title.', array('@num' => strlen($node->title))),
  );
  $seconds = $node->changed - $node->created;
  $information[] = array(
    'title' => t('Timestamp Differences'),
    'data' => t('The $node->changed timestamp is @seconds different from the $node->created timestamp.', array('@seconds' => $seconds)),
  );
  if ($node->translate == 0) {
    $information[] = array(
      'title' => t('Translated?'),
      'data' => t('This node is NOT translated into pirate speak, hope you know English.'),
    );
  }
  $information[] = array(
    'title' => t('Hash'),
    'data' => md5($node->title),
  );
  // Example of potential user input data that needs to be sanitized.
  // Instead of hard-coding this, it could be getting loaded from the db
  // where a user has entered the info.
  $information[] = array(
    'title' => t('Nefarious data'),
    'data' => '<a href="#" onclick="alert(\'Pwned!\'); return false;">click me</a>',
  );
  return $information;
}

Chamando nossa theme function


function spy_glass_page($node) {
  $information = _spy_glass_serious_spying_business($node);
  
  $content = array();
  foreach ($information as $item) {
    $content[] = array(
      '#type' => 'markup',
// custom theme function call
      '#markup' => theme('spy_glass_item', $item), 
    );
  }
  return $content;
}


hook_theme


registramos nosso theme item
/**
 * Implements hook_theme().
 */
function spy_glass_theme() {
  return array(
    'spy_glass_item' => array(
      'variables' => array('item' => NULL),
    ),
  );
}

a função que responde pelo theme item


responsável por produzir o output

function theme_spy_glass_item($variables) {
  $output = '<p>';
  $output .= '<strong>' . check_plain($variables['title']) . '</strong><br/>';
  $output .= check_plain($variables['data']);
  $output .= '</p>';
  return $output;
}

templates ao invez de função


Assim como theme functions podem retornar output para theme items, templates também

/**
 * Implements hook_theme().
 */
function spy_glass_theme() {
  return array(
    'spy_glass_item' => array(
      'arguments' => array('title' => NULL, 'data' => NULL),
      'template' => 'spy-glass-item',
    ),
  );
}

template


spy-glass-item.tpl.php

<p>
  <strong><?php print $title; ?></strong><br/>
  <?php print $data; ?>
  <em><?php print $module_name; ?></em>
</p>

Hook_preprocess


Podemos manipular variáveis antes de serem enviadas para o template

function spy_glass_preprocess_spy_glass_item(&$variables) {
  $variables['title'] = check_plain($variables['title']);
  $variables['data'] = check_plain($variables['data']);
  $variables['module_name'] = t('Spy Glass');
}

template ou função?


  • Use funções quando o output requer bastante lógica e pouco HTML
  • Use template quando o output possui bastante HTML e deixe a lógica no preprocess

o form api


  • Descreve formulários como arrays

  • O Drupal se encarrega do processo e workflow

    • Criação

    • Cacheamento

    • Validação

    • Submissão

  • Drupal da mais segurança

    • Comparação de input

    • Token

  • Drupal Rederiza e apresenta

criando formularios

hook_form
function form_fun_cake($form, &$form_state) {
  $form = array();

  $form['choice'] = array(
    '#type' => 'select',
    '#title' => t('Cake or pie?'),
    '#description' => t('Would you like cake or pie?'),
    '#options' => array(
      'cake' => t('Cake please'),
      'pie' => t('Pie I guess'),
    ),
    '#default_value' => 'cake',
    '#required' => TRUE,
  );

  $form['buttons']['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Submit'),
  );

  return $form;
}

validando formularios

hook_validate
recebe o build do formulário e seu estado atual

  • form_set_error e display de menssagens



function form_fun_cake_validate(&$form, &$form_state) {
  if ($form_state['values']['choice'] == 'cake') {
    form_set_error('choice', t('We are out of cake.'));
  }
}

SUBMISSÃO do formulario


hook_submit
recebe o build do form e estado atual

function form_fun_cake_submit(&$form, &$form_state) {
  dsm($form_state['values']);
  $form_state['redirect'] = '';
}

organizando elementos de formularios


  • fieldset element type
    • pode incluir N children
  • #tree attribute
    • Explicita relação entre elementos pai e filhos


organizando elementos do formulario


function form_fun_tree($form, &$form_state) {
  $form['#tree'] = TRUE;

  $form['home'] = array(
    '#type' => 'fieldset',
    '#title' => t('Home address'),
  );
  $form['home']['street'] = array(
    '#type' => 'textfield',
    '#title' => t('Street Address'),
  );
  $form['home']['city'] = array(
    '#type' => 'textfield',
    '#title' => t('City'),
  );

alteração de formularios


O FAPI fornece um hook para podermos alterar o formulário depois de construído.

hook_form_FORM_ID_alter(&$form, &$form_state)


function mymodule_form_alter(&$form, $form_state$form_id) {
  if ($form_id == 'user_login_block' || 
    $form_id == 'user_login') {
    // …
  }
}

 formulario de configuração o  módulo

/*
 * Sytems settings form for blacklisting passwords
 */
function demo_passwords_form($form, &$form_state) {
  $form['demo_badpasswords'] = array(
    '#type' => 'textfield',
    '#title' => t('Bad Passwords'),
    '#default_value' => variable_get('demo_badpasswords'),
    '#description' => t('Enter a list of "bad passwords" separated by commas.  If  a user chooses an of these, we'll throw a message),
  );
  return(system_settings_form($form));
}

overview do database layer


O Drupal nos abstrai do banco de dados, nos permitindo
fazer queries, adicionar e alterar tabelas.

queries no banco de dados com             db_select


 //SELECT COUNT FROM node WHERE type = $typename //AND status = $status
      $query = db_select('node', 'n')
        ->condition('type', $typename)
        ->condition('status', $status);
      $query->addExpression('COUNT (nid)', 'node_count');
      $results = $query->execute();
      $data = $results->fetchObject();

adicionando tabelas no banco de dados

/*
 *Implement hook_schema(). \
Insiderido numa ruqivo meumodulo.install
 */
function demo2_schema() {
  $schema = array();
  $schema['demo2'] = array(
    'description' => 'Information about node views.',
    'fields' => array(
      'nid' => array(
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
        'default' => 0,
        'description' => "The node's {node}.nid.",
      ),
      'uid' => array(
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
        'default' => 0,
        'description' => "The user's {uid}.uid.",
      ),
      'view_count' => array(
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
        'default' => 0,
        'description' => 'Simple node view counter per user.',
      ),
    ),
    'primary key' => array('nid', 'uid'),
  );
  return $schema;
}

adicionando tabelas no banco de dados


/*
 *Implements hook_update_N().
 */
function demo2_update_7000() {
  drupal_install_schema('demo2');
}

alteração do banco de dados

/**
* Implements hook_update_N().
*
* Add the "Last viewed date" field.
*/
function demo2_update_7001() {
  $field = array(
    'type' => 'int',
    'unsigned' => TRUE,
    'not null' => TRUE,
    'default' => 0,
    'description' => "The last views date (timestamp).",    
  );
  db_add_field('demo2', 'last_viewed', $field);

  return t('The "Last Viewed Date" field has been added.');
}

obrigado!

Vinicius Freitas
vinicius@taller.net.br
Made with Slides.com