Dotclear

Vous n'êtes pas identifié(e).

Annonce

13 février 2024 Sortie de Dotclear 2.29

#1 2011-12-01 08:48:45

cm-t
Membre
Lieu : Paris
Inscription : 2011-09-29
Site Web

Style des billets, éviter l'intrusion de style personalisé

Bonjour,


Je cherche une astuce pour éviter que les rédacteurs du blogue n'introduisent  un style personnalisé dans leur billet (cela se produit par exemple lorsqu'ils rédige depuis une éditeur de texte tiré d'une suite bureautique) et garder la charte graphique du blogue.

Jusqu'ici j'avais mis en place une méthode simple (dans /admin/post.php l. ~210) :

$cur->post_content = str_replace("style=", " ",  $post_content);

Mais la semaine dernière un rédacteur à voulu insérer une image depuis le wysiwyg, et cela insère un syle dans l'image qui sera nettoyer par le str_replace(); et donc l'image ne garde son alignement et son float.

Je suis en train de chercher une solution sur une regex mais comme je ne maitrise pas vraiment, je suis pour l'instant à la merci d'une bonne âme qui saurai me la construire. J'ai posté sur un autre forum (ou il y a un début de regex) mais je me disais que ça serai mieux de poster sur le forum sur lequel cela sera le plus utile.

Je suis partit sur une expression régulière, mais peut-être qu'il y a d'autre moyen efficace de le faire.

le top serai d'avoir la suppresion de "style=" compris dans toutes les balises sauf dans <img /> et sauf si c'est dans <pre> </pre> (cette dernière condition n'est pas indispensable pour moi mais serai utile en générale surtout dans un blogue où l'on poste du code)

Dernière modification par cm-t (2011-12-02 08:47:47)

Hors ligne

#2 2011-12-01 09:15:00

Jean-Michel
Modérateur à ailes d'ange
Lieu : Paris
Inscription : 2006-08-22
Site Web

Re : Style des billets, éviter l'intrusion de style personalisé

Bonjour,

La solution la plus simple, utiliser le mode wiki. A mon avis, nul besoin d'utiliser le mode xhtml si c'est pour arriver à ce genre d’extrémité ensuite.

Hors ligne

#3 2011-12-01 09:17:59

Philippe
Stagiaire
Lieu : Toulon
Inscription : 2004-06-13
Site Web

Re : Style des billets, éviter l'intrusion de style personalisé

La syntaxe wiki ne pose pas ces problèmes, ne peux-tu pas encourager tes rédacteurs à l'utiliser ?

Sinon, il vaut mieux ne pas modifier les fichiers de dotclear sous peine de se compliquer la vie lors des mises à jour... Il vaudrait mieux intervenir à l'affichage en ajoutant une balise de template dans ton thème. Par exemple pour supprimer les scripts tu peux ajouter ce code dans le fichier _public.php de ton thème :

$core->tpl->addValue('myExcerptWithoutScripts',array('myTpl','myExcerptWithoutScripts'));
$core->tpl->addValue('myContentWithoutScripts',array('myTpl','myContentWithoutScripts'));

class myTpl {
         public static function myExcerptWithoutScripts($attr)
	{
		$urls = '0';
		if (!empty($attr['absolute_urls'])) {
			$urls = '1';
		}
		
		$f = $GLOBALS['core']->tpl->getFilters($attr);
		return '<?php echo '.sprintf($f,'preg_replace("/<script[^>]*>.*?< *script[^>]*>/i","",$_ctx->posts->getExcerpt('.$urls.'))').'; ?>';
		
	}
	
	public static function myContentWithoutScripts($attr)
	{
		$urls = '0';
		if (!empty($attr['absolute_urls'])) {
			$urls = '1';
		}
		
		$f = $GLOBALS['core']->tpl->getFilters($attr);
		return '<?php echo '.sprintf($f,'preg_replace("/<script[^>]*>.*?< *script[^>]*>/i","",$_ctx->posts->getContent('.$urls.'))').'; ?>';
		
	}
}

que tu appelles ensuite dans ton fichier de template en remplaçant {{tpl:EntryExcerpt}} par {{tpl:myExcerptWithoutScripts}} pour l'extrait par exemple.

C'est juste la méthode, pour supprimer les attributs style sauf sur les images, ça va être un peu plus compliqué, peut-être quelqu'un a-t-il la regex toute prête sous le coude ?

Hors ligne

#4 2011-12-01 14:09:09

cm-t
Membre
Lieu : Paris
Inscription : 2011-09-29
Site Web

Re : Style des billets, éviter l'intrusion de style personalisé

@Jean-Michel : Le choix du mode xhtml a été fait pour ne pas faire peur aux rédacteurs et de ne pas les décourager dans leur énorme effort de vouloir participer au blogue, avec des balises étranges; et surtout grâce au wysiwyg pour les images, le redimensionnement. Passer en wiki résous en effet le soucis, mais je préfèrerai l'éviter pour les raisons cités ci-dessus.

@amalgame merci pour ce petit tuto, j'avoue qu'en plus de ce str_replace, toutes mes balises tpl personnalisées, je les ais mises dans /inc/public/class.dc.template.php au lieu de _public.php du theme; je vais passer par un petit nettoyage

peut-être quelqu'un a-t-il la regex toute prête sous le coude ?

j'avoue que ça me faciliterai bien des choses


edit:  je suis en train de tester la regex de sputnick, et je vois qu'on a eu la même erreur :P
edit2: il manquait l'échapement  \"

Dernière modification par cm-t (2011-12-01 14:38:35)

Hors ligne

#5 2011-12-01 18:44:52

Philippe
Stagiaire
Lieu : Toulon
Inscription : 2004-06-13
Site Web

Re : Style des billets, éviter l'intrusion de style personalisé

Après un peu de recherche, je parviens à une solution qui semble répondre à ton besoin : suppression des attributs style de toutes les balises sauf <img>. Voici la fonction :

$core->tpl->addValue('myContentWithoutStyles',array('myTpl','myContentWithoutStyles'));
class myTpl {
         public static function myContentWithoutStyles($attr)
	{
		$urls = '0';
		if (!empty($attr['absolute_urls'])) {
			$urls = '1';
		}
		$f = $GLOBALS['core']->tpl->getFilters($attr);
		
		return '<?php 
		$dom = new DOMDocument;
		$dom->loadHTML($_ctx->posts->getContent('.$urls.'));
		$xpath = new DOMXPath($dom);
		$nodes = $xpath->query(\'//*[@style]\');
		foreach($nodes as $node) {
			$name =  $node->nodeName;
			if ($name !=\'img\') {
				$node->removeAttribute(\'style\');
			}
		}
		$cleaned = preg_replace("/^<!DOCTYPE.+?>/", "", str_replace( array("<html>", "</html>", "<body>", "</body>"), array("", "", "", ""), $dom->saveHTML()));
		$cleaned = preg_replace("/<img([^>]*)>/i" , "<img $1 />", $cleaned);
		echo '.sprintf($f,'$cleaned').';
		?>';
	}
}

à appeler dans ton fichier de template avec {{tpl:myContentWithoutStyles}} et à dupliquer pour l'extrait ;)

Hors ligne

#6 2011-12-02 08:35:11

cm-t
Membre
Lieu : Paris
Inscription : 2011-09-29
Site Web

Re : Style des billets, éviter l'intrusion de style personalisé

Pour la solution avec la Regex:

$core->tpl->addValue('myContentWithoutStyle',array('myTpl','myContentWithoutStyle'));
class myTpl
{

	public static function myContentWithoutStyle($attr)
	{
		$urls = '0';
		if (!empty($attr['absolute_urls'])) {
			$urls = '1';
		}
		$f = $GLOBALS['core']->tpl->getFilters($attr);
		return '<?php echo '.sprintf($f,'preg_replace("/(<(?!img)\w+[^>]+)(style=\"[^\"]+\"|style=\'[^\']+\')([^>]*)(>)/","${1}${3}${4}",$_ctx->posts->getContent('.$urls.'))').'; ?>';
		
	}

Mais bon, après avoir lu pas mal de remarque sur loadHTML par rapport au regex dans le parsing XHTML, je crois que ta solution est la mieux approprié.

Merci beaucoup !

Dernière modification par cm-t (2011-12-02 08:36:33)

Hors ligne

#7 2011-12-02 08:51:15

Jean-Michel
Modérateur à ailes d'ange
Lieu : Paris
Inscription : 2006-08-22
Site Web

Re : Style des billets, éviter l'intrusion de style personalisé

Modération : sur ce forum, on a pas l'habitude de mettre de [Résolu].

Hors ligne

#8 2011-12-02 09:12:11

cm-t
Membre
Lieu : Paris
Inscription : 2011-09-29
Site Web

Re : Style des billets, éviter l'intrusion de style personalisé

@Jean-Michel mea culpa

@amalgame je regarde ta solution là, pbl de charset pour l'instant
je regarde ça http://www.php.net/manual/fr/domdocumen … .php#95251

edit:

Pour le problème utf-8:

$core->tpl->addValue('myContentWithoutStyle',array('myTpl','myContentWithoutStyle'));
class myTpl
{         public static function myContentWithoutStyles($attr)
	{
		$urls = '0';
		if (!empty($attr['absolute_urls'])) {
			$urls = '1';
		}
		$f = $GLOBALS['core']->tpl->getFilters($attr);
		
		return '<?php 
		$dom = new DOMDocument;
		$dom->loadHTML("<?xml encoding=\"UTF-8\">".$_ctx->posts->getContent('.$urls.'));
		foreach ($dom->childNodes as $item)
		if ($item->nodeType == XML_PI_NODE)
        $dom->removeChild($item);
		$doc->encoding = "UTF-8";
		$xpath = new DOMXPath($dom);
		$nodes = $xpath->query(\'//*[@style]\');
		foreach($nodes as $node) {
			$name =  $node->nodeName;
			if ($name !=\'img\') {
				$node->removeAttribute(\'style\');
			}
		}
		$cleaned = preg_replace("/^<!DOCTYPE.+?>/", "", str_replace( array("<html>", "</html>", "<body>", "</body>"), array("", "", "", ""), $dom->saveHTML()));
		$cleaned = preg_replace("/<img([^>]*)>/i" , "<img $1 />", $cleaned);
		echo '.sprintf($f,'$cleaned').';
		?>';
	}

edit: en fait j'ai encore un soucis dans certain billet:

Warning: DOMDocument::loadHTML() [domdocument.loadhtml]: Attribute target redefined in Entity, line: 1 in /dotclear/cache/cbtpl/2c/d5/2cd50d50fcfdcb3c766144954a48ca89.php on line 113

la ligne 113 étant la ligne générée:

$dom->loadHTML("<?xml encoding=\"UTF-8\">".$_ctx->posts->getContent(0));

Dernière modification par cm-t (2011-12-02 09:58:43)

Hors ligne

#9 2011-12-02 10:19:43

Philippe
Stagiaire
Lieu : Toulon
Inscription : 2004-06-13
Site Web

Re : Style des billets, éviter l'intrusion de style personalisé

Oui, c'est quand même du bricolage à la va-vite :P

Je pense qu'il faudrait utiliser loadXML (et saveXML($cleaned) pour la sortie), ce qui nous éviterait la regex superflue pour virer les balises inutiles ajoutées par loadHTML ;) et même peut-être la regex sur les images que j'ai rajoutée pour la validation XHTML...

Le souci est que $_ctx->posts->getContent n'est pas accepté par loadXML, il faudrait utiliser une classe de Dotclear (peut-être regarder comment est généré le flux RSS ?) pour le filtrer ;)

Dernière modification par Philippe (2011-12-02 10:21:03)

Hors ligne

#10 2011-12-02 14:15:41

Philippe
Stagiaire
Lieu : Toulon
Inscription : 2004-06-13
Site Web

Re : Style des billets, éviter l'intrusion de style personalisé

Voici une version plus propre et son application ici :

public static function myContentWithoutStyles($attr)
	{
		$urls = '0';
		if (!empty($attr['absolute_urls'])) {
			$urls = '1';
		}
		
		$f = $GLOBALS['core']->tpl->getFilters($attr);
		$open = '<div>';
		$close = '</div>';
		return '<?php 
		$dom = new DOMDocument;
		$dom->preserveWhiteSpace = false;
		$dom->substituteEntities = true;
		$dom->loadXML("'.$open.'".$_ctx->posts->getContent('.$urls.')."'.$close.'");
		$xpath = new DOMXPath($dom);
		$nodes = $xpath->query(\'//*[@style]\');
		foreach($nodes as $node) {
			$name =  $node->nodeName;
			if ($name !=\'img\') {
				$node->removeAttribute(\'style\');
			}
		}
		$content = $dom->saveXML();
		$cleaned = str_replace(\'<?xml version="1.0"?>\',\'\',$content);
		
		echo '.sprintf($f,'$cleaned').';
		?>';
		
	}

Je crois que saveXML() encode par défaut en UTF-8, il ne devrait donc plus y avoir de soucis à ce niveau.

Il y a encore un petit défaut : ça rajoute un bloc <div> englobant le tout (sinon loadXML couine^^), mais ça devrait être facile à enlever, ce sont les 5 premiers et les 6 derniers caractères de la chaîne ;)

Dernière modification par Philippe (2011-12-02 14:18:43)

Hors ligne

#11 2011-12-02 15:01:59

Philippe
Stagiaire
Lieu : Toulon
Inscription : 2004-06-13
Site Web

Re : Style des billets, éviter l'intrusion de style personalisé

Plus besoin de regex :

public static function myContentWithoutStyles($attr)
	{
		$urls = '0';
		if (!empty($attr['absolute_urls'])) {
			$urls = '1';
		}
		
		$f = $GLOBALS['core']->tpl->getFilters($attr);
		$open = '<div>';
		$close = '</div>';
		return '<?php 
		$dom = new DOMDocument;
		$dom->preserveWhiteSpace = false;
		$dom->substituteEntities = true;
		$dom->loadXML("'.$open.'".$_ctx->posts->getContent('.$urls.')."'.$close.'");
		$xpath = new DOMXPath($dom);
		$nodes = $xpath->query(\'//*[@style]\');
		foreach($nodes as $node) {
			$name =  $node->nodeName;
			if ($name !=\'img\') {
				$node->removeAttribute(\'style\');
			}
		}
		$content = $dom->saveXML($dom->firstChild);		
		echo '.sprintf($f,'$content').';
		?>';
		
	}

Dernière modification par Philippe (2011-12-02 15:08:19)

Hors ligne

#12 2011-12-02 16:43:27

Philippe
Stagiaire
Lieu : Toulon
Inscription : 2004-06-13
Site Web

Re : Style des billets, éviter l'intrusion de style personalisé

et pour virer le <div></div> englobant, il faut ajouter ceci avant le echo

$content = substr($content, strlen("'.$open.'")+strpos($content, "'.$open.'"), (strlen($content) - strpos($content, "'.$close.'"))*(-1));

edit : pff... marche pas s'il y a un <div></div> dans le contenu du billet...

Dernière modification par Philippe (2011-12-02 17:06:55)

Hors ligne

#13 2011-12-03 08:56:49

Philippe
Stagiaire
Lieu : Toulon
Inscription : 2004-06-13
Site Web

Re : Style des billets, éviter l'intrusion de style personalisé

Dernière version qui supprime effectivement la balise englobante (il suffisait de ne pas l'appeler <div>...)  :

public static function myContentWithoutStyles($attr)
	{
		$urls = '0';
		if (!empty($attr['absolute_urls'])) {
			$urls = '1';
		}
		
		$f = $GLOBALS['core']->tpl->getFilters($attr);
		$open = '<dummy>';
		$close = '</dummy>';
		return '<?php 
		$dom = new DOMDocument;
		$dom->preserveWhiteSpace = false;
		$dom->substituteEntities = true;
		$dom->loadXML("'.$open.'".$_ctx->posts->getContent('.$urls.')."'.$close.'");
		$xpath = new DOMXPath($dom);
		$nodes = $xpath->query(\'//*[@style]\');
		foreach($nodes as $node) {
			$name =  $node->nodeName;
			if ($name !=\'img\') {
				$node->removeAttribute(\'style\');
			}
		}
		$content = $dom->saveXML($dom->firstChild);
		$cleaned = substr($content, strlen("'.$open.'")+strpos($content, "'.$open.'"), (strlen($content) - strpos($content, "'.$close.'"))*(-1)); 
		echo '.sprintf($f,'$cleaned').';
		?>';
		
	}

En revanche, je ne vois pas de solution simple pour conserver les styles dans une balise particulière telle que pre ou code. Je pense que dans ce cas précis, tu devras distinguer le type de billet (avec par exemple un MyMeta=nofilter) afin qu'il ne passe pas par la moulinette ;)

Hors ligne

#14 2011-12-05 10:41:06

cm-t
Membre
Lieu : Paris
Inscription : 2011-09-29
Site Web

Re : Style des billets, éviter l'intrusion de style personalisé

edit: il y a un problème avec les espaces insécables:

J'ai essayé de m'inspiré de ça, en vain :'(

$open = '<XML
<?xml version=\'1.0\' encoding=\'utf-8\'?>
<!DOCTYPE root [
<!ENTITY nbsp \'&#160;\'>
]><dummy>';
$close = '</dummy> XML';

du coup j'ai mis un str_replace

$dom->loadXML("'.$open.'".str_replace("&nbsp;","&#160;",$_ctx->posts->getContent('.$urls.'))."'.$close.'");

merci encore amalgame !



_____

public static function myContentWithoutStyles($attr)
	{
		$urls = '0';
		if (!empty($attr['absolute_urls'])) {
			$urls = '1';
		}
		
		$f = $GLOBALS['core']->tpl->getFilters($attr);
		$open = '<dummy>';
		$close = '</dummy>';
		return '<?php 
		$dom = new DOMDocument;
		$dom->preserveWhiteSpace = false;
		$dom->substituteEntities = true;
		$dom->loadXML("'.$open.'".str_replace("&nbsp;","&#160;",$_ctx->posts->getContent('.$urls.'))."'.$close.'");
		$xpath = new DOMXPath($dom);
		$nodes = $xpath->query(\'//*[@style]\');
		foreach($nodes as $node) {
			$name =  $node->nodeName;
			if ($name !=\'img\') {
				$node->removeAttribute(\'style\');
			}
		}
		$content = $dom->saveXML($dom->firstChild);
		$cleaned = substr($content, strlen("'.$open.'")+strpos($content, "'.$open.'"), (strlen($content) - strpos($content, "'.$close.'"))*(-1)); 
		echo '.sprintf($f,'$cleaned').';
		?>';
		
	}

Dernière modification par cm-t (2011-12-05 10:45:41)

Hors ligne

Vous n'êtes pas identifié(e).

Pied de page des forums

Sites map