===== Rappel Gestion des actions dans l'administration sous dotclear 2.6 ====== Cette page décrit la migration du système d'ajout d'actions sur les listes de billets et/ou de commentaires via des plugins, depuis dotclear 2.5.X vers dotclear 2.6. A noter : dotclear 2.6 supporte toujours l'ancienne méthode, il est toutefois recommandé de passer à la nouvelle approche. La présente page décrit la méthode pour la gestion des actions sur les billets, mais elle est aussi applicable pour les commentaires. Nous nous appuierons sur un plugin d'example, dont l'objectif est de permettre d'autoriser ou pas les commentaires et les rétroliens sur une liste de billets. ==== Rappel : fonctionnement sous dotclear < 2.6 ==== Sous dotclear 2.5.x, il faut passer par 3 behaviors distincts : * adminPostsActionsCombo permet d'ajouter une entrée à la liste des actions * adminPostsActions permet de gérer le traitement des actions une fois soumises * adminPostsActionsContent permet d'afficher une page intermédiaire, en cas de besoin (dans notre plugin d'exemple, le formulaire de choix sur l'activation ou non des commentaires) * optionnellenemt, adminPostsActionsHeader permet d'enrichir le
de la page http, s'il faut inclure des styles ou des scripts particuliers dans la page intermédiaire === Structure globale du plugin === Le plugin, que nous appellerons OpenCloseComTB, se compose : * d'un fichier _define.php décrivant le plugin * d'un fichier _admin.php. La structure du fichier _admin.php est la suivante : addBehavior('adminPostsActionsCombo',array('AdminOpenCloseComTB','adminPostsActionsCombo')); $core->addBehavior('adminPostsActionsContent',array('AdminOpenCloseComTB','adminPostsActionsContent')); $core->addBehavior('adminPostsActions',array('AdminOpenCloseComTB','adminPostsActions')); class AdminOpenCloseComTB { public static function adminPostsActionsCombo($args) { [...] } public static function adminPostsActions($core,$posts,$action,$redir) { [...] } public static function adminPostsActionsContent($core,$action,$hidden_fields) { [...] } } ?> == adminPostsActionsCombo en détails == adminPostsActionsCombo prend en paramètre la liste des combos, que nous allons enrichir, dans la section "modifier" : public static function adminPostsActionsCombo($args) { $args[0][__('Change')] = array(__('Open/Close Comments or trackbacks') => 'openclosecomtb'); } La soumission de l'action "Open/Close Comments or trackback" enverra donc l'action 'openclosecomtb'. == adminPostsActionsContent en détails == adminPostsActionsContent prend 3 arguments : * $core : l'instance dcCode * $action : l'action actuellement soumise * $hidden_fields : les champs cachés à insérer dans le formulaire Ce qui donne : public static function adminPostsActionsContent($core,$action,$hidden_fields) { // On s'assure que l'action en cours nous concerne bien if ($action == 'openclosecomtb') { echo '

'.__('Open/close comments or trackbacos').'

'. '
'. '

'. form::checkbox('open_comm','1',''). '

'. '

'. form::checkbox('open_tb','1',''). '

'. // On n'oublie pas les champs cachés, le nonce et l'action $hidden_fields. $core->formNonce(). form::hidden(array('action'),'openclosecomtb'). // le champ changestatus nous permet de tester si le formulaire a bien été soumis form::hidden(array('changestatus'),'true'). '

'. '
'; } }
== adminPostsActions en détails == adminPostsActions prend 4 arguments : * $core : l'instance dcCode * $posts : le record contenant les billets sélectionnés * $action : l'action actuellement soumise * $redir : l'url de redirection une fois l'action exécutée On s'assure ici que le formulaire a bien été soumis en testant la présence du champ "changestatus" défini plus haut. Pour d'autre cas, il est possible de tester d'autre champs, mais ici nous n'avons que des checkbox, et une checkbox non cochée n'est pas définie dans $_POST. Ce qui donne : public static function adminPostsActions($core,$posts,$action,$redir) { if ($action == 'openclosecomtb' && !empty($_POST['changestatus']) && $core->auth->check('contentadmin',$core->blog->id)) { try { $set_co = isset($_POST['open_comm']); $set_tb = isset($_POST['open_tb']); while ($posts->fetch()) { $cur = $core->con->openCursor($core->prefix.'post'); $cur->post_open_tb = $set_tb?1:0; $cur->post_open_comment = $set_co?1:0; $cur->update('WHERE post_id = '.(integer) $posts->post_id); } http::redirect($redir); } catch (Exception $e) { $core->error->add($e->getMessage()); } } } ==== Fonctionnement sous dotclear 2.6 ==== Sous dotclear 2.6, un seul et unique behavior est nécessaire pour tout gérer : adminPostsActionsPage. Ce dernier véhicule un objet de contexte qui sera manipulé par les actions définies, une instance de dcPostsActionsPage. == dcPostsActionsPage == Cet objet définit le contexte de la page en cours. il expose les méthodes suivantes (seules les méthodes utiles aux plugins qui ajoutent des actions sont décrites ici): public function addAction ($actions,$callback) {} Ajoute une ou plusieurs actions, associées à un callback (voir plus loin). Le callback sera appelé si et seulement si une des actions définies en entrée a été sélectionnée. Pour la syntaxe de l'action, c'est la même que pour l'action de adminPostsActionsCombo. Paramètres : * $action : action(s) à ajouter. * $callback : callback à appeler. Exemples : # action simple sans catégorie: $ap->addAction( array(__('Myaction') => 'myaction'), array('myclass','mycallback') ); # plusieurs actions, dans le groupe "Toggle" $ap->addAction( array(__('Toggle') => array( __('enable') => 'myactionenable', __('disable') => 'myactiondisable')), array('myclass','mycallback') ); public function getIDs() {} Retourne la liste des identifiants des entrées sélectionnées (déjà filtrés au niveau sécurité) public function getHiddenFields($with_ids = false) {} Retourne les champs cachés à ajouter au formulaire, s'il faut en afficher un dans le plugin. Equivalent à $hidden_fields de l'ancien behavior adminPostsActionsContent. Si $with_ids est à true, les ids des billets cochés sont aussi inclus comme champs cachés. public function getRS() {} Retourne le record correspondant à la requête SQL de sélection des billets cochés. Equivalent à $posts de l'ancien behavior adminPostsActionsContent. public function redirect($with_selected_entries=false,$params=array()) {} Redirige vers la page appelante. Il est possible : * D'ajouter de nouveaux paramètres GET (ex : array('upd'=>1) pour avoir la notification de mise à jour) * d'ajouter les billets sélectionnés au GET, cela permet de garder les billets cochés sur la page de retour. Attention, en cas de suppression de billets, ils ne pourront plus être cochés par la suite :) public function getURI() {} Retourne l'uri du formulaire à utiliser, s'il faut afficher un formulaire intermédiaire. public function getAction() {} Retourne l'action qui a été sélectionnée (utile si plusieurs actions mènent au callback) public function getCheckboxes() {} Retourne un tableau contenant les billets de la sélection, qui sont à nouveau sélectionnables pour une seconde vérification Si vous créez un formulaire intermédiaire, vous serez amené à utiliser getHiddenFields aussi. 2 possiblités : * Vous voulez laisser la possibilité de désélectionner des billets, insérez getChecckboxes() et getHiddelFields() dans votre formulaire, * Vous ne voulez pas afficher la liste des billets, insérez alors getHiddenFields(true) dans votre formulaire. public function beginPage($breadcrumb='',$head=''); Affiche le début d'une page. A appeler en tout premier si vous voulez afficher un formulaire intermédiaire. $breadcrumb indique le titre de la page, $head permet d'ajouter des js ou des css à la page affichée public function endPage(); Le pendant de beginPage, à appeler en tout dernier si vous voulez afficher un formulaire intermédiaire == Les callbacks == Un callback doit avoir la forme suivante : public static function doCallback($core, dcPostsActionsPage $ap, $post) {} * $core est l'instance de dcCore actuelle * $ap est l'instance actionspage à exploiter * $post est une copie de $_POST, à préférer à ce dernier car il peut avoir évolué Attention ! $post n'a rien à voir avec le paramètre $posts de l'ancien behavior adminPostsActions == Guide du nouvel arrivant == Le traitement a une philosophie différente de l'ancien traitement : * on ne reçoit pas le record comme argument, il faut appeler $ap->getIDs ou $ap->getRS() * pas besoin de récupérer les exceptions, elles sont interceptées au dessus pour afficher une page d'erreur. Il est aussi recommandé de lever des exceptions (par exemple si aucun billet n'a été sélectionné) * Le callback est appelé uniquement si l'action a été sélectionnée, cela fait des tests inutiles de moins. Si plusieurs actions sont associées au callback, $ap->getAction() retourne celle qui a été sélectionnée * Tout l'affichage du formulaire intermédiaire (s'il en faut un) est à la charge du callback. Ne pas oublier d'appeler $ap->beginPage() et $ap->endPage() == Retour à l'exemple == Revenons à nos moutons. Notre _admin.php va ressembler à cela : addBehavior('adminPostsActionsPage',array('AdminOpenCloseComTB','adminPostsActionsPage')); class AdminOpenCloseComTB { public static function adminPostsActionsPage($core,$ap) { // Ajout de l'action à $ap [...] } public static function doOpenCloseComTB($core, dcPostsActionsPage $ap, $post) { // traitement de l'action [...] } } Ajoutons maintenant notre action : public static function adminPostsActionsPage($core,$ap) { $ap->addAction( array(__('Change') => array(__('Open/Close Comments or trackbacks') => 'openclosecomtb')), array('AdminOpenCloseComTB','doOpenCloseComTB') ); } ... et le callback qui va bien public static function doOpenCloseComTB($core, dcPostsActionsPage $ap, $post) { if (!empty($post['changestatus'])) { # changestatus non vide, le formulaire intermédiaire a été soumis # Les IDs des billets sont servis sur un plat $ids = $ap->getIDs(); # Pas de billets, pas bien! if (empty($ids)) { throw new Exception(__('No entry selected')); } # attention à utiliser $post et non $_POST $set_co = isset($post['open_comm']); $set_tb = isset($post['open_tb']); foreach ($ids as $id) { $cur = $core->con->openCursor($core->prefix.'post'); $cur->post_open_tb = $set_tb?1:0; $cur->post_open_comment = $set_co?1:0; $cur->update('WHERE post_id = '.(integer) $id); echo $id; } # Traitement fini, on redirige vers l'appelant avec une belle notification $ap->redirect(true,array('upd' => 1)); # Au diable les exceptions, c'est géré en amont :) } else { # Pas de 'changestatus', on affiche le formulaire intermédiaire # Ne pas oublier de commencer la page $ap->beginPage(); echo '

'.__('Open/close comments or trackbacks').'

'. '
'. # Une jolie sélection des billets pour être sûr $ap->getCheckboxes(). '

'. form::checkbox('open_comm','1',''). '

'. '

'. form::checkbox('open_tb','1',''). '

'. // On n'oublie pas les champs cachés, le nonce et l'action $ap->getHiddenFields(). $core->formNonce(). form::hidden(array('action'),'openclosecomtb'). // le champ changestatus nous permet de tester si le formulaire a bien été soumis form::hidden(array('changestatus'),'true'). '

'. '
'; # Et on ferme... $ap->endPage(); } } # Et c'est tout :)