xAMP
D'autres possibilités de base de données : Oracle, IIS, PostgreSQL
Languages que nous utiliserons :
On utilisera dans cette formation UwAmp ou n'importe quel autre déjà installé sur votre machine.
faire un tour de table des compétences et de l'expérience PHP
prévenir que le formateur ne peut pas intervenir en cas de problème sur autre chose que uwamp
Un bon terminal
sous Windows cmder basé sur ConEmu est très bien : http://cmder.net/
GIT
sous Windows, il est inclus dans cmder
Drush
que nous verrons comment installer plus tard
Un bon IDE sera utile surtout s'il a un débugguer intégré
nous verrons cela aussi plus tard
Installez un xAMP si vous n'en avez pas
Si vous en avez déjà un, sans XDebug, téléchargez UwAmp
http://www.uwamp.com/fr/
Installez GIT
Optionnellement (mais recommandé) installez un terminal alternatif à cmd ou utilisez PowerShell
<?php
non fermés : éviter l'envoi du bufferA connaître pour comprendre et être compris
* basés sur le standard PEAR
Avantages :
[xdebug]
xdebug.remote_enable=1
xdebug.remote_host=127.0.0.1
xdebug.remote_port=9000
xdebug.profiler_enable_trigger=1
L'option profiler_enable_trigger est en bonus si nous avons le tempsOptions :
La pluspart des modules pour développeur ne vous aideront pas ils vous feront perdre du temps
C'est pour cette raison que nous allons dès maintenant :
Un binaire pour Windows existe :
https://github.com/drush-ops/drush/releases/tag/7.0.0
Pour Linux, MacOS, etc... se reporter à la configuration en ligne.
Utilisé par beaucoup de développeurs dans le milieu du web
Très utile pour patcher des modules car utilisé pas la communauté Drupal et sur drupal.org
Une connaissance basique de quelques commandes suffit
Possibilité d'avoir une interface graphique : SourceTree, GitX, GitEye, Github
custom
System, User
-> prérequisNode, Field, Comment, File, Image
-> création de contenuText, List, Options, Number
-> types de champsMenu, Block, Taxonomie
-> structurationToolbar, Shortcut, Dashboard, Color, Overlay, Contextual
-> interfaceBook, Blog, Forum, Poll
-> structuration avancéeContact, Aggregator, Tracker, Update, OpenID
-> fonctions spécifiquesPath, RDF
-> SEOLocale, Content Translation
-> traductionDrupal sans ses modules contrib ne permet que de faire des sites simples.
Pour une version plus récente, utiliser le module jQuery Update
se familiariser avec PhpMyAdmin
identifier les tables des modules actuellement activés
identifier les autres : cache, variable, registry
identifier la table system et son utilité
.info
(https://drupal.org/node/542202)name
core
description
.module
.install
facultatif.test
facultatif.inc
facultatifCréer ce module : il doit apparaître dans la liste des modules.
Recense toutes les fonctions de Drupal, et leur documentation. Un IDE aura cette même documentation dans le code.
Aborde certains topics en profondeur : form API, schema API, hooks, etc
examples
Pour chaque concept de Drupal, des exemples concrets d'utilisation (ajax, form, blocks, cache, render, cron, dbtng, email, menu, node_access, theming, ...).
Ils sont fonctionnels : on peut les activer et voir leur impact en terme d'interface.
Très bien documentés, ne pas hésiter à lire, comprendre et reprendre le code de ces modules.
Implémenter hook_form_alter()
donnera mon_module_form_alter()
Poids des modules et altération
Liste des hooks https://api.drupal.org/api/drupal/includes%21module.inc/group/hooks/7
Plus de 350 hooks disponibles dans le cœur de Drupal
php -a
# Optionnel, mais évite des warnings PHP
$_SERVER['REMOTE_ADDR'] = '127.0.0.1';
# Bootstrappe Drupal, cf. index.php
define('DRUPAL_ROOT', getcwd());
require_once DRUPAL_ROOT . '/includes/bootstrap.inc';
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
print_r(module_implements('menu'));
# Force le cache à se charger
module_implements();
# Récupère le cache statique (en mémoire)
$impl = drupal_static('module_implements');
print_r(array_keys($impl));
drush php-eval "print_r(module_implements('menu'));"
Implémenter le hook_permission()
Créer les deux permissions
Vider le cache
Créer un role contributeur pouvant affecter le statut gold
Créer un role premium pouvant voir le statut gold
Créer un utilisateur pour chaque rôle
// Déclaration -> apparait dans la liste des blocks
function hook_block_info() {
$blocks['syndicate'] = array(
'info' => t('Syndicate'),
'cache' => DRUPAL_NO_CACHE,
);
return $blocks;
}
// Visualisation
function hook_block_view($delta = '') {
if ($delta == 'syndicate') {
return array(
'subject' => '',
'content' => '', // (Render array ou HTML)
);
}
}
// Altération
function hook_block_view_alter(&$data, $block) {}
function hook_block_view_MODULE_DELTA_alter(&$data, $block) {}
Créer un bloc :
gold-status
<h3>
Rappel: user_access() pour vérifier les permissions
Attention au cache d'implementations :
drush php-eval "cache_clear_all('cache_bootstrap');"
Les render arrays sont les blocs constituant une page Drupal. Ce sont des arrays PHP qui définissent des données (c-a-d la structure) ; par souci de modularité, on essaiera toujours de produire des render arrays. Ceci afin qu'ils soient puissent être modifiés via les hooks d'altérations ou par la couche de theming.
Les propriétés sont toujours préfixées par un #
et la propriété par défaut
est #markup
, elle permet d'indiquer du balisage simple.
Un render array est converti en HTML avec la fonction render();
// Un render array simple
'ma_cle1' => array(
'#markup' => "<h2>Du texte basique</h2>",
),
// Des propriétés utiles
'ma_cle2' => array(
'#markup' => "Du texte basique",
'#prefix' => '<h2>',
'#suffix' => '</h2>',
),
Une fonction de #theme
peut être renseignée ainsi que ses paramètres.
// Un render array qui produit un tableau HTML
'ma_cle1' => array(
'#theme' => 'table',
'#header' => $header,
'#rows' => $rows,
'#empty' => "Aucune donnée pour ce tableau",
),
Des propriétés utiles :
#cache
: mise en cache du render array#pre_render
: agit sur l'array avant le rendering#post_render
: agit sur le markup après le rendering#weight
: donne un poids à l'élément#attached
: lier à un ou des CSS/JS#access
: desactive l'élément si == FALSEExemples de fonctions de theme()
:
Liste complete des implementation de theme du cœur
$table_element = array(
'#theme' => 'image',
'#path' => drupal_get_path('module', 'monmodule') . '/monimage.png',
);
print drupal_render($table_element); // Quasi-automatiquement appelé
// par les hooks
Documentation http://drupal.org/node/930760 et le module render_example
hook_block_view_alter()
afin de changer le
<h3>
en <h4>
Ne pas hésiter à rendre le code lisible en utilisant des constantes pour les permissions
node/%
) à une action (afficher un noeud)node/123
est
un chemin, pointant vers la route node/%
où l'argument est 123)node/123
) vers un chemin arbitraire
renseigné par le contributeur (mon-noeud
)'access callback'
, 'access arguments'
%
' ou nommés '%node
' pour être chargés
à la volée avec *_load()
-> node_load()
MENU_NORMAL_ITEM
-> lien de menu dans l'arborescenceMENU_LOCAL_TASK
-> ongletMENU_CALLBACK
-> url simplepage callback
dans un fichier .inc
(file path + file)abc/def
est enfant de la route abc
si celle-ci est définiearguments
: utiliser un array()
page_callback
Propriété | Valeur par défaut |
---|---|
page callback | celle du parent |
access callback | user_access ou celle du parent |
file path | le chemin du module |
menu_name | navigation |
type | MENU_NORMAL_ITEM |
function mymodule_menu() {
$items['abc/def'] = array(
'title' => "My ABC page",
'page callback' => 'mymodule_abc_view',
'page arguments' => array(1),
'access callback' => 'mymodule_verify_access',
'access arguments' => array('une chaine'),
'file' => 'mymodule.pages.inc',
);
return $items;
}
function mymodule_abc_view($def) {
return "Mon contenu";
}
function mymodule_verify_access($string) {
return $string == 'une chaine';
}
Créer une page Suis-je Premium ? reproduisant le comportement du bloc
Créer une page Est-il Premium ? affichant la même chose, mais avec en argument l'uid de l'utilisateur
Créer une page Page Premium avec du contenu "Lorem Ipsum" et ne s'affichant que si l'utilisateur courant à la permission de voir le contenu premium
S'inspirer de la docummentation du hook_menu()
Placer toutes les callback dans un autre fichier que le .module
Attention au cache de menu
Quelques globales et fonctions de l'API à connaitre :
global $user
: utilisateur actuellement connecté
(modifier avec prudence !)node_load()
et node_load_multiple()
pour charger des nœudsnode_save()
pour enregistrer un nœuduser_load()
et user_load_multiple()
pour charger des utilisateursuser_save()
pour enregistrer un utilisateurREQUEST_TIME
: timestamp à l'appel de la pageLa fonction db_query()
permet d'exécuter du SQL directement mais utiliser des
accolades autour des noms de table {table_name}
(permet la gestion de
prefixe) et les placeholders pour passer des arguments (pour les failles de
sécurité). Utiliser ensuite foreach()
pour parcourir les résultats.
$result = db_query("SELECT some_col FROM {my_table}
WHERE some_col IN (:my_ids)", array(':my_ids' => $my_ids));
foreach ($result as $record) {
print_r($record);
}
Permet de faire des requêtes dynamiques grâce à une API en POO, donc sans manipuler de chaînes.
Documentation complète, voir également les commentaires sur chaque fonction sur https://api.drupal.org
$results = db_select('contact', 'c')
->fields('c')
->condition('created', REQUEST_TIME)
->execute()
->fetchAllAssoc('cid');
foreach ($results as $result) {
// faire qqch
}
Voir aussi db_insert()
, db_delete()
, db_update()
et db_merge()
Récupération de résultats :
Quelques globales et fonctions de l'API à connaitre :
url()
-> 'l()
-> lien avec texte (<a href=''>
)drupal_goto()
-> redirectionglobal $base_url
-> http://monsite.combase_path()
-> /
ou /mon-dossier-drupal
drupal_get_path()
(module, theme) -> chemin vers un module ou un thème
drupal_get_path('module', 'devel')
donne sites/all/modules/develCréer une page Utilisateurs premium listant les utilisateurs du site ayant un accès premium.
Créer une page
Récupérer les rôles ayant la permission de voir le contenu gold (dans la
table role_permission
)
Récupérer les utilisateurs ayant ces rôles (dans la table users_roles
)
Les afficher d'abord dans une liste
Modifier pour les afficher dans un tableau avec Identifiant, Nom, Lien vers son profil
Les callback doivent être situées dans un autre fichier que le .module
Attention au cache
Un formulaire est une structure déclarative composée d'éléments de la form API. La majeure partie des traitements est effectuée par celle-ci, rendant la création ou la modification de formulaire rapide et sécurisée.
Référentiel des composants disponibles sur api.drupal.org
$form = drupal_get_form('my_module_example_form'); // Appel
// Déclaration
function my_module_example_form($form, &$form_state) {
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Submit'),
);
return $form;
}
function my_module_example_form_validate($form, &$form_state) {
// Logique de validation.
}
function my_module_example_form_submit($form, &$form_state) {
// Traitement des données soumises.
}
Les données soumises et validées sont contenues dans $form_state['values']
.
Après exécution du _submit()
, l'utilisateur est redirigé vers le formulaire
vidé de ses valeurs, ou bien vers une page définie par $form_state['redirect']
Chaque formulaire a un identifiant unique qui permet de l'altérer facilement par les autres modules.
Schéma de workflow complet : https://drupal.org/files/fapi_workflow_7.x_v1.1.png
variable_set('name', value)
pour définirvariable_get('name', default_value)
pour récupérervariable_del('name')
pour supprimerBut: définir pour quels types de contenu la fonctionnalité premium est activée. C'est-à-dire, sur quels types on affichera l'option "Contenu premium" dans les formulaires de création ou de modification de nœud.
Article
Page de base
Valider le fait qu'on ne peut pas choisir le type de contenu Page de base
A la soumission enregistrer les valeurs dans une variable persistante
'gold_types'
Cocher les checkbox par défaut lorsque le type de contenu est activé
Un fichier n'est pas une entité ! (malheureusement)
Différence entre fichiers gérés et non gérés
Différence entre fichiers privés et fichiers publics
API complète https://api.drupal.org/api/drupal/includes%21file.inc/group/file/7
Un stream est un chemin, une URI, vers un fichier interne ou externe :
function hook_image_effect_info() {
$effects['mymodule_resize'] = array(
'label' => t('Resize'),
'help' => t('Resize an image to an exact set of dimensions.'),
'effect callback' => 'mymodule_resize_effect',
'dimensions callback' => 'mymodule_resize_dimensions',
'form callback' => 'mymodule_resize_form',
'summary theme' => 'mymodule_resize_summary',
);
return $effects;
}
function hook_image_default_styles() {
$styles['mymodule_preview'] = array(
'label' => 'My module preview',
'effects' => array(
array(
'name' => 'image_scale',
'data' => array(
'width' => 400,
'height' => 400,
'upscale' => 1,
),
'weight' => 0,
),
),
);
return $styles;
}
Celui-ci nous servira pour les images qui seront sur les articles gold
hook_schema()
-> crée une ou plusieurs tableshook_schema_alter()
déclare une modification (mais ne la réalise pas)hook_update_N()
servent à réaliser des actions sur la structure ou
les donnéesVia l'API, déclarer une table avec deux colonnes, nid etstatus.
Installer cette table via un hook_update_N()
ou réinstaller le module.
Ajouter la checkbox au dessus du titre
Enregistrer la valeur dans notre table à l'enregistrement
La mettre à sa valeur par défaut à l'affichage du formulaire
Rappels
hook_form_alter(&$form, $form_state, $form_id)
hook_node_update()
et hook_node_insert()
drupal_set_message('<pre>'.print_r($var, 1).'</pre>')
Utiliser l'API Node Access (documentation)
Déclarer notre hook_node_access()` et ne retourner NODE_ACCESS_DENY que si les 4 conditions sont réunies :
Attention: ne fonctionnera pas pour les listes (dont la page d'accueil)
pour des raisons de performances, il faudrait declarer hook_node_grants()
.
Rester simple et ne controler l'accès qu'à un nœud complet.
De même hook_node_access()
n'est pas appelé pour le superadmin.
// Déclaration du contenu du mail avec hook_mail().
function monmodule_mail($key, &$message, $params) {
$account = $params['account'];
$node = $params['node'];
$message['subject'] = "Bonjour " . format_username($account);
$message['body'][] = "Le noeud {$node->title} a été créé";
$message['body'][] = "A bientot";
}
// Envoi du mail.
drupal_mail('monmodule', 'maclé', 'toto@example.com', 'fr-FR', array(
'node' => node_load(123),
'user' => user_load_by_name('toto'),
);
On peut utiliser la notion de tokens (jetons) ou la fonction strtr().
'body
' est un tableau de lignes.
Envoi du mail avec drupal_mail($module, $key, $to, $language, $params = array())
;
La clé permet à un module de gérer plusieurs types de mail.
Envoyer un mail à tous les utilisateurs premium :
Sujet: Nouveau contenu premium Bonjour _Nom d'utilisateur_, Un nouveau _Type de contenu_ premium a été créé, il s'intitule _Titre_ Vous pouvez le consulter ici : _URL_ Cordialement, L'équipe du site _Nom du site_
<strong>Contenu premium/strong>
sur chaque contenu premiumLe module fournit toujours le markup par défaut.
Le hook_theme()
définit des hooks/clés qui pourront ensuite être utilisés via
la propriété #theme
des render arrays. Ces theme hooks généreront ensuite
le markup HTML soit via une fonction, soit via un template.
On peut fournir des variables à ces theme hooks. Des fonctions preprocess et process peuvent ajouter ou modifier des variables, et également ajouter des suggestions de templates.
https://www.drupal.org/files/theme_flow_6_1.pdf (pour Drupal 6)
function forum_theme() {
return array(
'forums' => array(
'template' => 'forums',
'variables' => array(
'forums' => NULL,
'topics' => NULL,
),
),
);
}
$html_output = theme('forums', array(
'forums' => $forums,
'topics' => $topics,
));
// Mais toujours privilégier les render arrays, car altérables
$build['forums'] = array(
'#theme' => 'forums',
'#forums' => $forums,
'#topics' => $topics,
);
Convertir le render array utilisé dans hook_node_view()
pour utiliser un
theme hook qui se base sur un template.
Passer en paramètre le nœud et transformer "Contenu premium" en "Titre du nœud est un contenu premium".
Ajouter une image de médaille dans ce template.
Créer un preprocess pour transformer cette image en variable.
Dans le preprocess, transformer l'image en render array, et utiliser la fonciton render() dans le template.
Ajouter des suggestions pour ce template.
.info
-> globalAjouter une classe à l'image et créer un fichier CSS:
img.gold-icon {
float: right;
}
L'ajouter lorsqu'un noœud gold est affiché.
Voir les modules example
Quelques exemples
Une bonne pratique de développement
TP: créer un profil avec notre module gold
Drush make
Les performances
Features
Table of contents | t |
---|---|
Exposé | ESC |
Autoscale | e |
Full screen slides | f |
Presenter view | p |
Source files | s |
Slide numbers | n |
Blank screen | b |
Notes | 2 |
Help | h |