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.

Note :

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 <header> 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 :

<?php
$core->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
			'<h2>'.__('Open/close comments or trackbacos').'</h2>'.
 
			'<form action="posts_actions.php" method="post">'.
 
			'<p>'.
			form::checkbox('open_comm','1','').
			' <label class="classic" for="open_comm">'.__('Allow Comments').'</label></p>'.
			'<p>'.
			form::checkbox('open_tb','1','').
			' <label class="classic" for="open_comm">'.__('Allow Trackbacks').'</label></p>'.
 
			// 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').
			'<p><input type="submit" value="'.__('Ok').'" /></p>'.
			'</form>';
		}
	}
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

Astuce :

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 :

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 :

<?php
$core->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
				'<h2>'.__('Open/close comments or trackbacks').'</h2>'.
 
				'<form action="'.$ap->getURI().'" method="post">'.
				# Une jolie sélection des billets pour être sûr
				$ap->getCheckboxes().
 
				'<p>'.
				form::checkbox('open_comm','1','').
				' <label class="classic" for="open_comm">'.__('Allow Comments').'</label></p>'.
				'<p>'.
				form::checkbox('open_tb','1','').
				' <label class="classic" for="open_comm">'.__('Allow Trackbacks').'</label></p>'.
 
				// 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').
				'<p><input type="submit" value="'.__('Ok').'" /></p>'.
				'</form>';
			# Et on ferme...
			$ap->endPage();
		}
	}
	# Et c'est tout :)

Wiki powered by Dokuwiki.