Changeset 118855 in spip-zone
- Timestamp:
- Dec 2, 2019, 12:30:19 PM (7 days ago)
- Location:
- _plugins_/facteur/trunk
- Files:
-
- 3 added
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
_plugins_/facteur/trunk
- Property subgit:lock:5a870185582ca35b2eb360fd507ce156b0d8c2c8 deleted
-
Property
subgit:lock:00dd4501ded3fbc7b1b4c54a5a0a0364ab69d5da
set to
2019-12-02T18:21:48.553
-
_plugins_/facteur/trunk/facteur_fonctions.php
r118853 r118855 24 24 25 25 /** 26 * Transformer un mail texte ou HTML simplifie en mail HTML complet avec le wrapper emails/texte.html27 * Si le mail est un mail texte :28 * la premiere ligne est le sujet29 * le reste est le corps du mail30 *31 * Si le mail est un mail HTML simplifie :32 * le sujet est entre <title></title>33 * le corps est entre <body></body>34 * une eventuelle intro peut etre fournie entre <intro></intro>35 *36 * @param string $texte_ou_html37 * @return string38 */39 function facteur_email_wrap_to_html($texte_ou_html){40 $texte_ou_html = trim($texte_ou_html);41 // attention : si pas de contenu on renvoi du vide aussi (mail vide = mail vide)42 if (!strlen(trim($texte_ou_html))) {43 return $texte_ou_html;44 }45 46 $contexte = array('sujet' => '', 'texte' => '', 'intro' => '');47 48 // tester si le mail est en html (simplifie)49 if (substr($texte_ou_html, 0, 1) == '<'50 and substr($texte_ou_html,-1,1) == '>'51 and stripos($texte_ou_html, '</body>') !== false) {52 53 // dans ce cas on ruse un peu : extraire le sujet du title54 $sujet = '';55 if (preg_match(",<title>(.*)</title>,Uims", $texte_ou_html, $m)) {56 $contexte['sujet'] = $m[1];57 $texte_ou_html = preg_replace(",<title>(.*)</title>,Uims", '', $texte_ou_html, 1);58 $texte_ou_html = trim($texte_ou_html);59 }60 if (preg_match(",<intro>(.*)</intro>,Uims", $texte_ou_html, $m)){61 $contexte['intro'] = $m[1];62 $texte_ou_html = preg_replace(",<intro>(.*)</intro>,Uims", '', $texte_ou_html, 1);63 $texte_ou_html = trim($texte_ou_html);64 }65 $contexte['html'] = preg_replace(",</?body>,ims", '', $texte_ou_html);66 }67 else {68 // la premiere ligne est toujours le sujet69 $texte_ou_html = explode("\n", $texte_ou_html);70 $contexte['sujet'] = trim(array_shift($texte_ou_html));71 $contexte['texte'] = trim(implode("\n", $texte_ou_html));72 }73 74 // attention : si pas de contenu on renvoi du vide aussi (mail vide = mail vide)75 if (!strlen(trim(implode('', $contexte)))) {76 return '';77 }78 79 return recuperer_fond('emails/texte', $contexte);80 }81 82 /*83 84 Written by Eric Dols - edols@auditavenue.com85 86 You may freely use or modify this, provided87 you leave credits to the original coder.88 Feedback about (un)successfull uses, bugs and improvements done89 are much appreciated, but don't expect actual support.90 91 PURPOSE OF THIS FUNCTION92 It is designed to process html emails relying93 on a css stylesheet placed in the <head> for layout in94 order to enhance compatibility with email clients,95 including webmail services.96 Provided you use minimal css, you can keep styling separate97 from the content in your email template, and let this function98 "inject" those styles inline in your email html tags on-the-fly,99 just before sending.100 Technically, it grabs the style declarations found in the101 <head> section and inserts each declaration inline,102 inside the corresponding html tags in the email message.103 104 Supports both HTML and XHTML markup seamlessly. Thus105 tolerant to email message writers using non-xhtml tag,106 even when template is xhtml compliant (e.g. they would107 add <img ...> instead of a xhtml compliant <img ... />).108 109 NEW 10 dec. 2003:110 - code revised, including a few regexp bugs fixed.111 - multiple class for a tag are now allowed <p class="firstclass secondclass">112 - all unsupported css styles are now moved to the body section (not just a:hover etc...)113 114 USE115 Add this function to a function library include, like "inline.inc"116 and include it near the beginning of your php page:117 require ("inline.inc");118 119 load the html source of message into a variable120 like $html_source and process it using:121 $html_source = sheet2inline($html_source)122 123 124 STYLE DEFINITIONS SUPPORTED125 TAG { ... }126 TAG1, TAG2, ... { ... }127 TAG.class { ... }128 .class { ...)129 TAG:pseudo { ... }130 131 132 CSS definitions may be freely formatted (spaces, tabs, linefeeds...),133 they are converted to oneliners before inserting them inline in the html tags.134 135 .class definitions are processed AFTER tag definitions,136 thus appended inline after any existing tag styling to137 preserve the normal css priority behavior.138 139 Existing style="..." attributes in tags are NOT stripped. However they MUST140 be with double quotes. If not, an addtional style="..." attribute will be added141 142 143 KNOWN LIMITATIONS144 - style info should be placed in <head> section. I believe145 it shouldnt be too hard to modify to point to an external146 stylesheet instead.147 - no support (yet?):148 * chains like P UL LI { .... } or P UL LI.class { .... }149 * #divname p { ... } and <tag id="...">150 * a:hover, a:visited {...} multiple class:pseudo151 They require a significantly more complicated processing likely152 based on stylesheet and document trees parsing.153 Many email clients don't handle more than what is supported154 by this script anyway.155 - pseudo-classes like a:hover {...} can't be inserted inline156 in the html tags: they are moved to a <style> declaration in157 the <body> instead. This is a limitation from html, not this script.158 - It is still up to you to check if target email clients render159 your css styled templates correctly, especially webmail services160 like Hotmail, in which the email becomes a sub-part of an html page,161 with styles already in place.162 */163 function facteur_convertir_styles_inline($body){164 // variables to be accessed in the callback sub-function too165 global $styledefinition, $styletag, $styleclass;166 167 // Let's first load the stylesheet information in a $styles array using a regexp168 preg_match_all ( "/^[ \t]*([.]?)([\w, #]+)([.:])?(\S*)\s+{([^}]+)}/mi", $body , $styles);169 /*170 $styles[1] = . or '' => .class or tag (empty)171 $styles[2] = name of class or tag(s)172 $styles[3] = : . or '' => followed by pseudo-element, class separator or nothing (empty)173 $styles[4] = name of pseudo-element after a tag, if any174 $styles[5] = the style definition itself, i.e. what's between the { }175 */176 177 // Now loop through the styles found and act accordingly;178 179 // process TAG {...} & TAG1, TAG2,... {...} definitions only first by order of appearance180 foreach ($styles[1] as $i => $type) {181 if ($type=="" && $styles[3][$i]=="") {182 $styledefinition = trim($styles[5][$i]);183 $styletag = preg_replace("/ *, */", "|", trim($styles[2][$i])); //echo $styletag."<br />";184 $styleclass = "";185 // process TAG {...} and TAG1, TAG2 {...} but not TAG1 TAG2 {...} or #divname styles186 if (!preg_match("/ /", $styletag) && !preg_match("/#/", $styletag)) {187 $pattern = "!<(".$styletag.")([^>]*(?= /)|[^>]*)( /)?>!mi";188 $body = preg_replace_callback ($pattern, 'facteur_addstyle' , $body);189 $styles[6][$i]=1; // mark as injected inline190 }191 }192 }193 194 // append additional .CLASS {...} and TAG.CLASS {...} styling by order of appearance195 // important to do so after TAG {...} definitions, so that class attributes override TAG styles when needed196 foreach ($styles[1] as $i => $type) {197 if ($type!="." && $styles[3][$i]=="." ) { // class definition for a specific tag198 $styledefinition = trim($styles[5][$i]);199 $styletag = trim($styles[2][$i]);200 $styleclass = trim($styles[4][$i]);201 $pattern = "!<(".$styletag.")([^>]* class\=['\"][^'\"]*".$styleclass."[^'\"]*['\"][^>]*(?= /)|[^>]* class\=['\"][^'\"]*".$styleclass."[^'\"]*['\"][^>]*)( />)?>!mi";202 $body = preg_replace_callback ($pattern, 'facteur_addstyle' , $body);203 $styles[6][$i]=1; // mark as injected inline204 205 }206 elseif ($type=="." && $styles[3][$i]=="" ) { // general class definition for any tag207 $styledefinition = trim($styles[5][$i]);208 $styletag = "";209 $styleclass = trim($styles[2][$i]);210 $pattern = "!<(\w+)([^>]* class\=['\"]".$styleclass."['\"][^>]*(?= /)|[^>]* class\=['\"]".$styleclass."['\"][^>]*)( />)?>!mi";211 $body = preg_replace_callback ($pattern, 'facteur_addstyle' , $body);212 $styles[6][$i]=1; // mark as injected inline213 }214 }215 216 217 /* move all style declarations that weren't injected from <head> to a <body> <style> section,218 including but not limited to:219 - pseudo-classes like a:hover {...} as they can't be set inline220 - declaration chains like UL LI {...}221 - #divname {...}. These are not supported by email clients like Mac/Entourage anyway, it seems. */222 foreach ($styles[1] as $i => $type) {223 if ($styles[6][$i]=="") {224 // add a <style type="text/css"> section after <body> if there's isn't one yet225 if (preg_match ("!<body[^>]*>\s*<style!mi", $body)==0) {226 $body = preg_replace ("/(<body[^>]*>)/i", "\n\$1\n".'<style type="text/css">'."\n<!--\n-->\n</style>\n", $body);227 }228 // append a copy of the pseudo-element declaration to that body style section229 $styledefinition = trim($styles[5][$i]);230 $styledefinition = preg_replace ("!\s+!mi", " ", $styledefinition ); // convert style definition to a one-liner (optional)231 $declaration = $styles[1][$i].trim($styles[2][$i]).$styles[3][$i].trim($styles[4][$i])." { ".$styledefinition." }";232 $body = preg_replace ("!(<body[^>]*>\s*<style[^>]*>\s*<\!\-\-[^>]*)"."(\s*\-\->\s*</style>)!si", "\$1".$declaration."\n\$2", $body);233 $styles[6][$i]= 2; // mark as moved to <style> section in <body>234 }235 }236 237 // remove stylesheet declaration(s) from <head> section (comment following line out if not wanted)238 //$body = preg_replace ("!(<head>.*)<style type.*</style>(.*</head>)!si", "\$1\$2" , $body);239 240 // check what styles have been injected241 # print_r($styles);242 243 return $body;244 }245 246 /**247 * facteur_addstyle248 * @author Eric Dols249 *250 * @param $matches251 * @return string252 */253 function facteur_addstyle($matches) {254 255 // $matches[1]=tag, $matches[2]=tag attributes (if any), $matches[3]=xhtml closing (if any)256 257 // variables values set in calling function258 global $styledefinition, $styletag, $styleclass;259 260 // convert the style definition to a one-liner261 $styledefinition = preg_replace ("!\s+!mi", " ", $styledefinition );262 // convert all double-quotes to single-quotes263 $styledefinition = preg_replace ('/"/','\'', $styledefinition );264 265 if (preg_match ("/style\=/i", $matches[2])) {266 // add styles to existing style attribute if any already in the tag267 $pattern = "!(.* style\=)[\"]([^\"]*)[\"](.*)!mi";268 $replacement = "\$1".'"'."\$2 ".$styledefinition.'"'."\$3";269 $attributes = preg_replace ($pattern, $replacement , $matches[2]);270 } else {271 // otherwise add new style attribute to tag (none was present)272 $attributes = $matches[2].' style="'.$styledefinition.'"';273 }274 275 if ($styleclass!="") {276 // if we were injecting a class style, remove the now useless class attribute from the html tag277 278 // Single class in tag case (class="classname"): remove class attribute altogether279 $pattern = "!(.*) class\=['\"]".$styleclass."['\"](.*)!mi";280 $replacement = "\$1\$2";281 $attributes = preg_replace ( $pattern, $replacement, $attributes);282 283 // Multiple classes in tag case (class="classname anotherclass..."): remove class name from class attribute.284 // classes are injected inline and removed by order of appearance in <head> stylesheet285 // exact same behavior as where last declared class attributes in <style> take over (IE6 tested only)286 $pattern = "!(.* class\=['\"][^\"]*)(".$styleclass." | ".$styleclass.")([^\"]*['\"].*)!mi";287 $replacement = "\$1\$3";288 $attributes = preg_replace ( $pattern, $replacement, $attributes);289 290 }291 292 return "<".$matches[1].$attributes.$matches[3].">";293 }294 295 /**296 26 * Un filtre pour transformer les retour ligne texte en br si besoin (si pas autobr actif) 297 27 * … … 306 36 } 307 37 38 39 308 40 /** 309 * Transformer un mail HTML en mail Texte proprement : 310 * - les tableaux de mise en page sont utilisés pour structurer le mail texte 311 * - le reste du HTML est markdownifie car c'est un format texte lisible et conventionnel 41 * voir inc/facteur_mail_wrap_to_html 312 42 * 43 * @param string $texte_ou_html 44 * @return string 45 */ 46 function facteur_email_wrap_to_html($texte_ou_html){ 47 48 $facteur_mail_wrap_to_html = charger_fonction('facteur_mail_wrap_to_html', 'inc'); 49 return $facteur_mail_wrap_to_html($texte_ou_html); 50 } 51 52 /** 53 * voir inc/facteur_convertir_styles_inline 54 * 55 * @param string $body 56 * @return string 57 */ 58 function facteur_convertir_styles_inline($body){ 59 60 $facteur_convertir_styles_inline = charger_fonction('facteur_convertir_styles_inline', 'inc'); 61 return $facteur_convertir_styles_inline($body); 62 } 63 64 65 /** 66 * voir inc/facteur_mail_html2text 313 67 * @param string $html 314 68 * @return string 315 69 */ 316 70 function facteur_mail_html2text($html){ 317 // nettoyer les balises de mise en page html318 $html = preg_replace(",</(td|th)>,Uims","<br/>",$html);319 $html = preg_replace(",</(table)>,Uims","@@@hr@@@",$html);320 $html = preg_replace(",</?(html|body|table|td|th|tbody|thead|center|article|section|span)[^>]*>,Uims","\n\n",$html);321 71 322 // commentaires html et conditionnels 323 $html = preg_replace(",<!--.*-->,Uims","\n",$html); 324 $html = preg_replace(",<!\[.*\]>,Uims","\n",$html); 325 326 $html = preg_replace(",<(/?)(div|tr|caption)([^>]*>),Uims","<\\1p>",$html); 327 $html = preg_replace(",(<p>\s*)+,ims","<p>",$html); 328 $html = preg_replace(",<br/?>\s*</p>,ims","</p>",$html); 329 $html = preg_replace(",</p>\s*<br/?>,ims","</p>",$html); 330 $html = preg_replace(",(</p>\s*(@@@hr@@@)?\s*)+,ims","</p>\\2",$html); 331 $html = preg_replace(",(<p>\s*</p>),ims","",$html); 332 333 // succession @@@hr@@@<hr> et <hr>@@@hr@@@ 334 $html = preg_replace(",@@@hr@@@\s*(<[^>]*>\s*)?<hr[^>]*>,ims","@@@hr@@@\n",$html); 335 $html = preg_replace(",<hr[^>]*>\s*(<[^>]*>\s*)?@@@hr@@@,ims","\n@@@hr@@@",$html); 336 337 $html = preg_replace(",<textarea[^>]*spip_cadre[^>]*>(.*)</textarea>,Uims","<code>\n\\1\n</code>",$html); 338 339 // vider le contenu de qqunes : 340 $html = preg_replace(",<head[^>]*>.*</head>,Uims","\n",$html); 341 342 // Liens : 343 // Nettoyage des liens des notes de bas de page 344 $html = preg_replace("@<a href=\"#n(b|h)[0-9]+-[0-9]+\" name=\"n(b|h)[0-9]+-[0-9]+\" class=\"spip_note\">([0-9]+)</a>@", "\\3", $html); 345 // Supprimer tous les liens internes 346 $html = preg_replace("/\<a href=['\"]#(.*?)['\"][^>]*>(.*?)<\/a>/ims","\\2", $html); 347 // Remplace tous les liens 348 preg_match_all("/\<a href=['\"](.*?)['\"][^>]*>(.*?)<\/a>/ims", $html,$matches,PREG_SET_ORDER); 349 $prelinks = $postlinks = array(); 350 if (!function_exists('url_absolue')) 351 include_spip('inc/filtres'); 352 foreach ($matches as $k => $match){ 353 $link = "@@@link$k@@@"; 354 $url = str_replace("&","&",$match[1]); 355 if ($match[2]==$match[1] OR $match[2]==$url){ 356 // si le texte est l'url : 357 $prelinks[$match[0]] = "$link"; 358 } 359 else { 360 // texte + url 361 $prelinks[$match[0]] = $match[2] . " ($link)"; 362 } 363 // passer l'url en absolu dans le texte sinon elle n'est pas clicable ni utilisable 364 $postlinks[$link] = url_absolue($url); 365 } 366 $html = str_replace(array_keys($prelinks), array_values($prelinks),$html); 367 368 // les images par leur alt ? 369 // au moins les puces 370 $html = preg_replace(',<img\s[^>]*alt="-"[^>]*>,Uims','-',$html); 371 // les autres 372 $html = preg_replace(',<img\s[^>]*alt=[\'"]([^\'"]*)[\'"][^>]*>,Uims',"\\1",$html); 373 // on vire celles sans alt 374 $html = preg_replace(",</?(img)[^>]*>,Uims","\n",$html); 375 376 // espaces 377 $html = str_replace(" "," ",$html); 378 $html = preg_replace(",<p>\s+,ims","<p>",$html); 379 380 #return $html; 381 include_spip("lib/markdownify/markdownify"); 382 $parser = new Markdownify('inline',false,false); 383 $texte = $parser->parseString($html); 384 385 $texte = str_replace(array_keys($postlinks), array_values($postlinks),$texte); 386 387 388 // trim et sauts de ligne en trop ou pas assez 389 $texte = trim($texte); 390 $texte = str_replace("<br />\n","\n",$texte); 391 $texte = preg_replace(",(@@@hr@@@\s*)+\Z,ims","",$texte); 392 $texte = preg_replace(",(@@@hr@@@\s*\n)+,ims","\n\n\n".str_pad("-",75,"-")."\n\n\n",$texte); 393 $texte = preg_replace(",(\n#+\s),ims","\n\n\\1",$texte); 394 $texte = preg_replace(",(\n\s*)(\n\s*)+(\n)+,ims","\n\n\n",$texte); 395 396 397 // <p> et </p> restants 398 $texte = str_replace(array("<p>","</p>"),array("",""),$texte); 399 400 // entites restantes ? (dans du code...) 401 include_spip('inc/charsets'); 402 $texte = unicode2charset($texte); 403 $texte = str_replace(array(''', '"'),array("'",'"'), $texte); 404 405 406 // Faire des lignes de 75 caracteres maximum 407 return trim(wordwrap($texte)); 72 $facteur_mail_html2text = charger_fonction('facteur_mail_html2text', 'inc'); 73 return $facteur_mail_html2text($html); 408 74 }
Note: See TracChangeset
for help on using the changeset viewer.