PageRenderTime 34ms CodeModel.GetById 1ms RepoModel.GetById 1ms app.codeStats 0ms

/main/class/parser/parser_wysiwyg.php

http://github.com/FSB/Fire-Soft-Board-2
PHP | 475 lines | 325 code | 70 blank | 80 comment | 67 complexity | 3f595da8e398a0b0fc0ed0fe0c6c0017 MD5 | raw file
  1. <?php
  2. /**
  3. * Fire-Soft-Board version 2
  4. *
  5. * @package FSB2
  6. * @author Genova <genova@fire-soft-board.com>
  7. * @version $Id$
  8. * @license http://opensource.org/licenses/gpl-2.0.php GNU GPL 2
  9. */
  10. /**
  11. * Gestion de l'encodage / decodage des messages pour le WYSIWYG
  12. */
  13. class Parser_wysiwyg extends Fsb_model
  14. {
  15. /**
  16. * Tag HTML ouvert
  17. */
  18. const TAG_OPEN = 1;
  19. /**
  20. * Tag HTML ferme
  21. */
  22. const TAG_CLOSE = 2;
  23. /**
  24. * Texte entre deux tags HTML
  25. */
  26. const TEXT = 3;
  27. /**
  28. * Parse des FSBcode uniquement affichables sur le WYSIWYG
  29. *
  30. * @param string $str
  31. * @return string
  32. */
  33. public static function decode($str)
  34. {
  35. $str = htmlspecialchars($str);
  36. $str = str_replace(array("\r\n", "\n", "[br]"), array("<br />", "<br />", "<br />"), $str);
  37. $fsbcode = new Parser_fsbcode();
  38. $fsbcode->only_wysiwyg = true;
  39. $str = $fsbcode->parse($str);
  40. $str = Parser::smilies($str, true);
  41. return ($str);
  42. }
  43. /**
  44. * Parse une chaine de caractere HTML pour la transformer en FSBcode
  45. *
  46. * @param string $str
  47. * @return string
  48. */
  49. public static function encode($str)
  50. {
  51. //echo "<xmp>$str</xmp><hr />";
  52. // On ferme quelques balises
  53. $str = preg_replace('#<img(.*?)>#i', '<img\\1></img>', $str);
  54. // On remplace les &nbsp; par des espaces
  55. $str = str_replace('&nbsp;', ' ', $str);
  56. // Represente la pile dans laquelle sera mis le texte a reconstruire
  57. $stack = array();
  58. // On recupere l'ensemble des balises HTML de la chaine
  59. preg_match_all('#<(/)?([a-zA-Z_][a-zA-Z0-9_]*?)( .*?)?>#s', $str, $tokens, PREG_OFFSET_CAPTURE);
  60. $count_tokens = count($tokens[0]);
  61. for ($i = 0, $offset = 0, $id = 1; $i < $count_tokens; $i++)
  62. {
  63. $state = ($tokens[1][$i][0] == '/') ? self::TAG_CLOSE : self::TAG_OPEN;
  64. $name = strtolower($tokens[2][$i][0]);
  65. $length = strlen($tokens[0][$i][0]);
  66. $text = substr($str, $offset, $tokens[0][$i][1] - $offset);
  67. // Ajout du texte a la pile de donnees
  68. if ($text)
  69. {
  70. $stack[] = array(
  71. 'type' => self::TEXT,
  72. 'content' => $text,
  73. );
  74. }
  75. switch ($state)
  76. {
  77. // Parse des tags fermes
  78. case self::TAG_CLOSE :
  79. // On parcourt la pile a la recherche de la premiere balise ouverte du meme type qu'on trouve (et non fermee)
  80. $count_stack = count($stack);
  81. for ($j = $count_stack - 1, $find_id = 0; $j >= 0; $j--)
  82. {
  83. // On ajoute la fermeture du tag a la pile si : on tombe sur le premier tag ouvert ayant le meme nom que le tag qu'on ferme
  84. // actuellement ou si on tombe sur un tag ouvert avec une ID similaire a celle du tag qu'on ferme
  85. if ($stack[$j]['type'] == self::TAG_OPEN && $stack[$j]['tag'] == $name && ($find_id == 0 || $find_id == $stack[$j]['id']) && isset($stack[$j]['close']))
  86. {
  87. $find_id = $stack[$j]['id'];
  88. $stack[] = array(
  89. 'type' => self::TAG_CLOSE,
  90. 'content' => $stack[$j]['close'],
  91. );
  92. // On supprime l'indice 'close' pour montrer que ces tags ont ete fermes (on pourra ainsi les afficher)
  93. unset($stack[$j]['close']);
  94. }
  95. // Petite optimisation pour ne pas parcourir le reste de la pile inutilement, puisqu'on a recupere ce dont on avait besoin
  96. if ($find_id != 0 && (!isset($stack[$j]['id']) || $find_id != $stack[$j]['id']))
  97. {
  98. break;
  99. }
  100. }
  101. break;
  102. // Parse des tags ouverts
  103. case self::TAG_OPEN :
  104. // Recuperation des attributs dans un tableau
  105. $attr = array();
  106. if (is_array($tokens[3][$i]))
  107. {
  108. // Merci IE pour ton code XHTML si propre ...
  109. if (preg_match('#size=([0-9]+?)#i', $tokens[3][$i][0], $m))
  110. {
  111. $attr['size'] = $m[1];
  112. $tokens[3][$i] = preg_replace('#size=([0-9]+?)#i', '', $tokens[3][$i][0]);
  113. }
  114. preg_match_all('#\s([a-zA-Z_]+?)=\\\"([^"]*?)\\\"#', $tokens[3][$i][0], $m);
  115. $count_attr = count($m[0]);
  116. for ($j = 0; $j < $count_attr; $j++)
  117. {
  118. $attr[strtolower($m[1][$j])] = $m[2][$j];
  119. }
  120. }
  121. // Ici on empile les balises qui seront ajoute a $stack
  122. $s = array();
  123. // Transformation des tags en style
  124. switch ($name)
  125. {
  126. // Gras
  127. case 'b' :
  128. case 'strong' :
  129. $s[] = 'b';
  130. break;
  131. // Italique
  132. case 'i' :
  133. case 'em' :
  134. $s[] = 'i';
  135. break;
  136. // Souligne
  137. case 'u' :
  138. $s[] = 'u';
  139. break;
  140. // Barre
  141. case 'strike' :
  142. $s[] = 'strike';
  143. break;
  144. // Code informatique
  145. case 'code' :
  146. $s[] = 'code' . ((isset($attr['args'])) ? '=' . $attr['args'] : '');
  147. break;
  148. // Citation
  149. case 'blockquote' :
  150. $s[] = 'quote';
  151. break;
  152. // Liste
  153. case 'ul' :
  154. $s[] = 'list';
  155. break;
  156. // Liste ordonnee
  157. case 'ol' :
  158. if (isset($attr['style']) && preg_match('#list-style-type: disc;#i', $attr['style']))
  159. {
  160. $s[] = 'list';
  161. }
  162. else
  163. {
  164. $s[] = 'list=1';
  165. }
  166. break;
  167. // Puce de liste
  168. case 'li' :
  169. $stack[] = array(
  170. 'type' => self::TAG_OPEN,
  171. 'content' => '[*]',
  172. 'tag' => $name,
  173. 'id' => $id,
  174. );
  175. break;
  176. // Images
  177. case 'img' :
  178. if (!isset($attr['src']))
  179. {
  180. break;
  181. }
  182. $url = $attr['src'];
  183. $args = ':';
  184. // Apparament Firefox aime bien faire des choses qu'on ne lui demande pas de faire, d'ou
  185. // ce code pour fixer les bons chemins vers les URL locales http://localhost/fsb2/tpl/WhiteSummer/img/logo.gif
  186. if (isset($attr['realsrc']))
  187. {
  188. $url = $attr['realsrc'];
  189. }
  190. if (isset($attr['alt']))
  191. {
  192. $args .= 'alt=' . $attr['alt'] . ',';
  193. }
  194. if (isset($attr['title']))
  195. {
  196. $args .= 'title=' . $attr['title'] . ',';
  197. }
  198. if (isset($attr['width']))
  199. {
  200. $args .= 'width=' . $attr['width'] . ',';
  201. }
  202. else if (isset($attr['style']) && preg_match('#width: ([0-9]+)px#i', $attr['style'], $m))
  203. {
  204. $args .= 'width=' . $m[1] . ',';
  205. }
  206. if (isset($attr['height']))
  207. {
  208. $args .= 'height=' . $attr['height'] . ',';
  209. }
  210. else if (isset($attr['style']) && preg_match('#height: ([0-9]+)px#i', $attr['style'], $m))
  211. {
  212. $args .= 'height=' . $m[1] . ',';
  213. }
  214. $args = substr($args, 0, -1);
  215. $s[] = 'img' . $args;
  216. $s[] = '__text__=' . $url;
  217. break;
  218. // Liens hypertexte
  219. case 'a' :
  220. if (!isset($attr['href']))
  221. {
  222. break;
  223. }
  224. $url = $attr['href'];
  225. // Apparament Firefox aime bien faire des choses qu'on ne lui demande pas de faire, d'ou
  226. // ce code pour fixer les bons chemins vers les URL locales http://localhost/fsb2/tpl/WhiteSummer/img/logo.gif
  227. if (isset($attr['realsrc']))
  228. {
  229. $url = $attr['realsrc'];
  230. }
  231. // Adresse email ?
  232. if (preg_match('#^mailto:#i', $url))
  233. {
  234. $s[] = 'mail=' . substr($url, 7);
  235. }
  236. else
  237. {
  238. $s[] = 'url=' . $url;
  239. }
  240. break;
  241. // Sauts de ligne
  242. case 'br' :
  243. $stack[] = array(
  244. 'type' => self::TAG_OPEN,
  245. 'content' => "\n",
  246. 'tag' => '',
  247. 'id' => $id,
  248. );
  249. break;
  250. // Fichiers joints
  251. case 'div' :
  252. if (isset($attr['type']) && $attr['type'] == 'attach')
  253. {
  254. $s[] = 'attach' . ((isset($attr['args'])) ? '=' . $attr['args'] : '');
  255. }
  256. break;
  257. }
  258. // Parse du style
  259. if (isset($attr['style']))
  260. {
  261. $split = explode(';', $attr['style']);
  262. foreach ($split AS $style)
  263. {
  264. $split2 = explode(':', $style);
  265. if (count($split2) == 2)
  266. {
  267. $property = strtolower(trim($split2[0]));
  268. $value = trim($split2[1]);
  269. switch ($property)
  270. {
  271. case 'font-weight' :
  272. if ($value == 'bold')
  273. {
  274. $s[] = 'b';
  275. }
  276. break;
  277. case 'font-style' :
  278. if ($value == 'italic')
  279. {
  280. $s[] = 'i';
  281. }
  282. break;
  283. case 'text-decoration' :
  284. switch ($value)
  285. {
  286. case 'underline' :
  287. $s[] = 'u';
  288. break;
  289. case 'line-through' :
  290. $s[] = 'strike';
  291. break;
  292. case 'underline line-through' :
  293. case 'line-through underline' :
  294. $s[] = 'u';
  295. $s[] = 'strike';
  296. break;
  297. }
  298. break;
  299. case 'color' :
  300. case 'background-color' :
  301. $color = $value;
  302. if (preg_match('#^rgb\(([0-9]{1,3}), ?([0-9]{1,3}), ?([0-9]{1,3})\)#i', $value, $m))
  303. {
  304. $color = '#' . String::add_zero(dechex($m[1]), 2) . String::add_zero(dechex($m[2]), 2) . String::add_zero(dechex($m[3]), 2);
  305. }
  306. $tagname = ($property == 'color') ? 'color' : 'bgcolor';
  307. $s[] = $tagname . '=' . $color;
  308. break;
  309. case 'font-family' :
  310. $s[] = 'font=' . $value;
  311. break;
  312. case 'font-size' :
  313. if (preg_match('#^([0-9]+)px$#i', $value, $m))
  314. {
  315. $s[] = 'size=' . $m[1];
  316. }
  317. break;
  318. case 'text-align' :
  319. if (in_array(strtolower($value), array('center', 'left', 'right', 'justify')))
  320. {
  321. $s[] = 'align=' . $value;
  322. }
  323. break;
  324. }
  325. }
  326. }
  327. }
  328. // Gestion des couleurs
  329. if (isset($attr['color']))
  330. {
  331. $s[] = 'color=' . $attr['color'];
  332. }
  333. // Gestion de la taille de police
  334. if (isset($attr['size']))
  335. {
  336. $size = intval($attr['size']);
  337. $tmp_size = array('1' => '8', '2' => '10', '3' => '16', '5' => '20', '6' => '24');
  338. $size = (!isset($tmp_size[$size])) ? $tmp_size[3] : $tmp_size[$size];
  339. $s[] = 'size=' . $size;
  340. }
  341. // Gestion de l'alignement du texte
  342. if (isset($attr['align']) && preg_match('#^(left|center|right|justify)$#i', $attr['align'], $m))
  343. {
  344. $s[] = 'align=' . $m[1];
  345. }
  346. // Gestion de la police
  347. if (isset($attr['face']))
  348. {
  349. $s[] = 'font=' . $attr['face'];
  350. }
  351. // Ajout de $s a $stack
  352. foreach ($s AS $v)
  353. {
  354. $close = $v;
  355. if (preg_match('#^([a-z_]+?)(:|=)(.*?)$#i', $close, $m))
  356. {
  357. $close = $m[1];
  358. }
  359. if ($close == '__text__')
  360. {
  361. $stack[] = array(
  362. 'type' => self::TEXT,
  363. 'content' => $m[3],
  364. );
  365. }
  366. else
  367. {
  368. $stack[] = array(
  369. 'type' => self::TAG_OPEN,
  370. 'content' => '[' . $v . ']',
  371. 'tag' => $name,
  372. 'id' => $id,
  373. 'close' => '[/' . $close . ']',
  374. );
  375. }
  376. }
  377. $id++;
  378. break;
  379. }
  380. // On deplace l'offset de lecture du texte a la fin de la balise
  381. $offset = $tokens[0][$i][1] + $length;
  382. }
  383. // Ajout de la derniere partie du message
  384. $stack[] = array(
  385. 'type' => self::TEXT,
  386. 'content' => substr($str, $offset),
  387. );
  388. // Reconstruction du message
  389. $return = '';
  390. foreach ($stack AS $line)
  391. {
  392. // Les tags ouverts et non fermes ne sont pas mis dans le texte
  393. if ($line['type'] == self::TAG_OPEN && isset($line['close']))
  394. {
  395. continue;
  396. }
  397. $return .= $line['content'];
  398. }
  399. $return = String::unhtmlspecialchars($return);
  400. //echo "<xmp>$return</xmp><hr />";
  401. //exit;
  402. return ($return);
  403. }
  404. }
  405. /* EOF */