source: spip-zone/_plugins_/facteur/trunk/classes/facteur.php @ 54242

Last change on this file since 54242 was 54242, checked in by cedric@…, 8 years ago

PHP4 n'est plus supporte

  • Property svn:executable set to *
File size: 18.0 KB
Line 
1<?php
2/*
3 * Plugin Facteur
4 * (c) 2009-2010 Collectif SPIP
5 * Distribue sous licence GPL
6 *
7 */
8
9if (!defined("_ECRIRE_INC_VERSION")) return;
10
11include_spip('inc/charsets');
12include_spip('inc/filtres');
13
14if (!class_exists('PHPMailer')) {
15        include_spip('phpmailer-php5/class.phpmailer');
16        include_spip('phpmailer-php5/class.smtp');
17}
18
19include_spip('facteur_fonctions');
20
21class Facteur extends PHPMailer {
22
23        function Facteur($email, $objet, $message_html, $message_texte) {
24
25                if ($GLOBALS['meta']['facteur_adresse_envoi'] == 'oui'
26                  AND $GLOBALS['meta']['facteur_adresse_envoi_email'])
27                        $this->From = $GLOBALS['meta']['facteur_adresse_envoi_email'];
28                else
29                        $this->From = $GLOBALS['meta']['email_webmaster'];
30
31                if ($GLOBALS['meta']['facteur_adresse_envoi'] == 'oui'
32                  AND $GLOBALS['meta']['facteur_adresse_envoi_nom'])
33                        $this->FromName = $GLOBALS['meta']['facteur_adresse_envoi_nom'];
34                else
35                        $this->FromName = strip_tags(extraire_multi($GLOBALS['meta']['nom_site']));
36
37                $this->CharSet = "utf-8";
38                $this->Mailer = 'mail';
39                $this->Subject = unicode_to_utf_8(charset2unicode($objet,$GLOBALS['meta']['charset']));
40
41                //Pour un envoi multiple de mail, $email doit être un tableau avec les adresses.
42                if (is_array($email)) {
43                        foreach ($email as $cle => $adresseMail) {
44                                $this->AddAddress($adresseMail);
45                        }
46                }
47                else
48                        $this->AddAddress($email);
49
50                if (!empty($GLOBALS['meta']['facteur_smtp_sender'])) {
51                        $this->Sender = $GLOBALS['meta']['facteur_smtp_sender'];
52                        $this->AddCustomHeader("Errors-To: ".$this->Sender);
53                }
54
55                if (!empty($GLOBALS['meta']['facteur_cc'])) {
56                        $this->AddCC( $GLOBALS['meta']['facteur_cc'] );
57                }
58                if (!empty($GLOBALS['meta']['facteur_bcc'])) {
59                        $this->AddBCC( $GLOBALS['meta']['facteur_bcc'] );
60                }
61               
62                if (isset($GLOBALS['meta']['facteur_smtp']) AND $GLOBALS['meta']['facteur_smtp'] == 'oui') {
63                        $this->Mailer   = 'smtp';
64                        $this->Host     = $GLOBALS['meta']['facteur_smtp_host'];
65                        $this->Port     = $GLOBALS['meta']['facteur_smtp_port'];
66                        if ($GLOBALS['meta']['facteur_smtp_auth'] == 'oui') {
67                                $this->SMTPAuth = true;
68                                $this->Username = $GLOBALS['meta']['facteur_smtp_username'];
69                                $this->Password = $GLOBALS['meta']['facteur_smtp_password'];
70                        }
71                        else {
72                                $this->SMTPAuth = false;
73                        }
74                        if (intval(phpversion()) == 5) {
75                        if ($GLOBALS['meta']['facteur_smtp_secure'] == 'ssl')
76                                $this->SMTPSecure = 'ssl';
77                        if ($GLOBALS['meta']['facteur_smtp_secure'] == 'tls')
78                                $this->SMTPSecure = 'tls';
79                        }
80                }
81
82                if (!empty($message_html)) {
83                        $message_html = unicode_to_utf_8(charset2unicode($message_html,$GLOBALS['meta']['charset']));
84                        $this->Body = $message_html;
85                        $this->IsHTML(true);
86                        if ($GLOBALS['meta']['facteur_filtre_css'])
87                                $this->ConvertirStylesEnligne();
88                        if ($GLOBALS['meta']['facteur_filtre_images'])
89                                $this->JoindreImagesHTML();
90                        $this->UrlsAbsolues();
91                }
92                if (!empty($message_texte)) {
93                        $message_texte = unicode_to_utf_8(charset2unicode($message_texte,$GLOBALS['meta']['charset']));
94                        if (!$this->Body) {
95                                $this->IsHTML(false);
96                                $this->Body = $message_texte;
97                        }
98                        else {
99                                $this->AltBody = $message_texte;
100                        }
101                }
102
103                if ($GLOBALS['meta']['facteur_filtre_iso_8859'])
104                        $this->ConvertirUtf8VersIso8859();
105
106        }
107       
108        /*
109         * Transforme du HTML en texte brut, mais proprement, c'est-à-dire en essayant
110         * de garder les titrages, les listes, etc
111         *
112         * @param string $html Le HTML à transformer
113         * @return string Retourne un texte brut formaté correctement
114         */
115        function html2text($html){
116                // On remplace tous les sauts de lignes par un espace
117                $html = str_replace("\n", ' ', $html);
118               
119                // Supprimer tous les liens internes
120                $texte = preg_replace("/\<a href=['\"]#(.*?)['\"][^>]*>(.*?)<\/a>/ims", "\\2", $html);
121       
122                // Supprime feuille style
123                $texte = preg_replace(";<style[^>]*>[^<]*</style>;i", "", $texte);
124       
125                // Remplace tous les liens     
126                $texte = preg_replace("/\<a[^>]*href=['\"](.*?)['\"][^>]*>(.*?)<\/a>/ims", "\\2 (\\1)", $texte);
127       
128                // Les titres
129                $texte = preg_replace(";<h1[^>]*>;i", "\n= ", $texte);
130                $texte = str_replace("</h1>", " =\n\n", $texte);
131                $texte = preg_replace(";<h2[^>]*>;i", "\n== ", $texte);
132                $texte = str_replace("</h2>", " ==\n\n", $texte);
133                $texte = preg_replace(";<h3[^>]*>;i", "\n=== ", $texte);
134                $texte = str_replace("</h3>", " ===\n\n", $texte);
135               
136                // Une fin de liste
137                $texte = preg_replace(";</(u|o)l>;i", "\n\n", $texte);
138               
139                // Une saut de ligne *après* le paragraphe
140                $texte = preg_replace(";<p[^>]*>;i", "\n", $texte);
141                $texte = preg_replace(";</p>;i", "\n\n", $texte);
142                // Les sauts de ligne interne
143                $texte = preg_replace(";<br[^>]*>;i", "\n", $texte);
144       
145                //$texte = str_replace('<br /><img class=\'spip_puce\' src=\'puce.gif\' alt=\'-\' border=\'0\'>', "\n".'-', $texte);
146                $texte = preg_replace (';<li[^>]*>;i', "\n".'- ', $texte);
147       
148       
149                // accentuation du gras
150                // <b>texte</b> -> **texte**
151                $texte = preg_replace (';<b[^>]*>;i','**' ,$texte);
152                $texte = str_replace ('</b>','**' ,$texte);
153       
154                // accentuation du gras
155                // <strong>texte</strong> -> **texte**
156                $texte = preg_replace (';<strong[^>]*>;i','**' ,$texte);
157                $texte = str_replace ('</strong>','**' ,$texte);
158       
159       
160                // accentuation de l'italique
161                // <em>texte</em> -> *texte*
162                $texte = preg_replace (';<em[^>]*>;i','/' ,$texte);
163                $texte = str_replace ('</em>','*' ,$texte);
164               
165                // accentuation de l'italique
166                // <i>texte</i> -> *texte*
167                $texte = preg_replace (';<i[^>]*>;i','/' ,$texte);
168                $texte = str_replace ('</i>','*' ,$texte);
169       
170                $texte = str_replace('&oelig;', 'oe', $texte);
171                $texte = str_replace("&nbsp;", " ", $texte);
172                $texte = filtrer_entites($texte);
173       
174                // On supprime toutes les balises restantes
175                $texte = supprimer_tags($texte);
176       
177                $texte = str_replace("\x0B", "", $texte); 
178                $texte = str_replace("\t", "", $texte) ;
179                $texte = preg_replace(";[ ]{3,};", "", $texte);
180       
181                // espace en debut de ligne
182                $texte = preg_replace("/(\r\n|\n|\r)[ ]+/", "\n", $texte);
183       
184                //marche po
185                // Bring down number of empty lines to 4 max
186                $texte = preg_replace("/(\r\n|\n|\r){3,}/m", "\n\n", $texte);
187       
188                //saut de lignes en debut de texte
189                $texte = preg_replace("/^(\r\n|\n|\r)*/", "\n\n", $texte);
190                //saut de lignes en debut ou fin de texte
191                $texte = preg_replace("/(\r\n|\n|\r)*$/", "\n\n", $texte);
192       
193                // Faire des lignes de 75 caracteres maximum
194                //$texte = wordwrap($texte);
195       
196                return $texte;
197        }
198       
199        /**
200         * Transformer les urls des liens et des images en url absolues
201         * sans toucher aux images embarquees de la forme "cid:..."
202         */
203        function UrlsAbsolues(){
204                include_spip('inc/filtres_mini');
205                if (preg_match_all(',(<(a|link)[[:space:]]+[^<>]*href=["\']?)([^"\' ><[:space:]]+)([^<>]*>),imsS',
206                $this->Body, $liens, PREG_SET_ORDER)) {
207                        foreach ($liens as $lien) {
208                                if (strncmp($lien[3],"cid:",4)!==0){
209                                        $abs = url_absolue($lien[3], $base);
210                                        if ($abs <> $lien[3] and !preg_match('/^#/',$lien[3]))
211                                                $this->Body = str_replace($lien[0], $lien[1].$abs.$lien[4], $this->Body);
212                                }
213                        }
214                }
215                if (preg_match_all(',(<(img|script)[[:space:]]+[^<>]*src=["\']?)([^"\' ><[:space:]]+)([^<>]*>),imsS',
216                $this->Body, $liens, PREG_SET_ORDER)) {
217                        foreach ($liens as $lien) {
218                                if (strncmp($lien[3],"cid:",4)!==0){
219                                        $abs = url_absolue($lien[3], $base);
220                                        if ($abs <> $lien[3])
221                                                $this->Body = str_replace($lien[0], $lien[1].$abs.$lien[4], $this->Body);
222                                }
223                        }
224                }
225        }
226
227        function JoindreImagesHTML() {
228                $image_types = array(
229                                                        'gif'   => 'image/gif',
230                                                        'jpg'   => 'image/jpeg',
231                                                        'jpeg'  => 'image/jpeg',
232                                                        'jpe'   => 'image/jpeg',
233                                                        'bmp'   => 'image/bmp',
234                                                        'png'   => 'image/png',
235                                                        'tif'   => 'image/tiff',
236                                                        'tiff'  => 'image/tiff',
237                                                        'swf'   => 'application/x-shockwave-flash'
238                                                );
239                while (list($key,) = each($image_types))
240                        $extensions[] = $key;
241
242                preg_match_all('/["\'](([^"\']+)\.('.implode('|', $extensions).'))([?][^"\']+)?["\']/Ui', $this->Body, $images, PREG_SET_ORDER);
243
244                $html_images = array();
245                foreach($images as $im){
246                        if (!preg_match(",^[a-z0-9]+://,i",$im[1])
247                         AND ($src = $im[1].$im[4])
248                         AND (
249                              file_exists($f=$im[1]) // l'image a ete generee depuis le meme cote que l'envoi
250                              OR (_DIR_RACINE AND file_exists($f=_DIR_RACINE.$im[1])) // l'image a ete generee dans le public et on est dans le prive
251                              OR (!_DIR_RACINE AND strncmp($im[1],"../",3)==0 AND file_exists($f=substr($im[1],3))) // l'image a ete generee dans le prive et on est dans le public
252                             )
253                         AND !isset($html_images[$src])){
254
255                                $extension = strtolower($im[3]);
256                                $header_extension = $image_types[$extension];
257                                $cid = md5($f); // un id unique pour un meme fichier
258                                // l'ajouter si pas deja present (avec un autre ?...)
259                                if (!in_array($cid,$html_images))
260                                        $this->AddEmbeddedImage($f, $cid, basename($f),'base64',$header_extension);
261                                $this->Body = str_replace($src, "cid:$cid", $this->Body);
262                                $html_images[$src] = $cid; // marquer l'image comme traitee, inutile d'y revenir
263                        }
264                }
265        }
266
267
268        function ConvertirStylesEnligne() {
269                /*
270
271                Written by Eric Dols - edols@auditavenue.com
272
273                You may freely use or modify this, provided
274                you leave credits to the original coder.
275                Feedback about (un)successfull uses, bugs and improvements done
276                are much appreciated, but don't expect actual support.
277
278                PURPOSE OF THIS FUNCTION
279                        It is designed to process html emails relying
280                        on a css stylesheet placed in the <head> for layout in
281                        order to enhance compatibility with email clients,
282                        including webmail services.
283                        Provided you use minimal css, you can keep styling separate
284                        from the content in your email template, and let this function
285                        "inject" those styles inline in your email html tags on-the-fly,
286                        just before sending.
287                        Technically, it grabs the style declarations found in the
288                        <head> section and inserts each declaration inline,
289                        inside the corresponding html tags in the email message.
290
291                        Supports both HTML and XHTML markup seamlessly. Thus
292                        tolerant to email message writers using non-xhtml tag,
293                        even when template is xhtml compliant (e.g. they would
294                        add <img ...> instead of a xhtml compliant <img ... />).
295
296                NEW 10 dec. 2003:
297                        - code revised, including a few regexp bugs fixed.
298                        - multiple class for a tag are now allowed <p class="firstclass secondclass">
299                        - all unsupported css styles are now moved to the body section (not just a:hover etc...)
300
301                USE
302                        Add this function to a function library include, like "inline.inc"
303                        and include it near the beginning of your php page:
304                        require ("inline.inc");
305
306                        load the html source of message into a variable
307                        like $html_source and process it using:
308                        $html_source = sheet2inline($html_source)
309
310
311                STYLE DEFINITIONS SUPPORTED
312                        TAG { ... }
313                        TAG1, TAG2, ... { ... }
314                        TAG.class { ... }
315                        .class { ...)
316                        TAG:pseudo { ... }
317
318
319                        CSS definitions may be freely formatted (spaces, tabs, linefeeds...),
320                        they are converted to oneliners before inserting them inline in the html tags.
321
322                        .class definitions are processed AFTER tag definitions,
323                        thus appended inline after any existing tag styling to
324                        preserve the normal css priority behavior.
325
326                        Existing style="..." attributes in tags are NOT stripped. However they MUST
327                        be with double quotes. If not, an addtional style="..." attribute will be added
328
329
330                KNOWN LIMITATIONS
331                        - style info should be placed in <head> section. I believe
332                                it shouldnt be too hard to modify to point to an external
333                                stylesheet instead.
334                        - no support (yet?):
335                                * chains like P UL LI { .... } or P UL LI.class { .... }
336                                * #divname p { ... } and <tag id="...">
337                                * a:hover, a:visited {...} multiple class:pseudo
338                                They require a significantly more complicated processing likely
339                                based on stylesheet and document trees parsing.
340                                Many email clients don't handle more than what is supported
341                                by this script anyway.
342                        - pseudo-classes like a:hover {...} can't be inserted inline
343                                in the html tags: they are moved to a <style> declaration in
344                                the <body> instead. This is a limitation from html, not this script.
345                        - It is still up to you to check if target email clients render
346                                your css styled templates correctly, especially webmail services
347                                like Hotmail, in which the email becomes a sub-part of an html page,
348                                with styles already in place.
349                */
350
351                // variables to be accessed in the callback sub-function too
352                global $styledefinition, $styletag, $styleclass;
353
354                // Let's first load the stylesheet information in a $styles array using a regexp
355                preg_match_all ( "/^[ \t]*([.]?)([\w, #]+)([.:])?(\S*)\s+{([^}]+)}/mi", $this->Body , $styles);
356                /*
357                        $styles[1] = . or ''  => .class or tag (empty)
358                        $styles[2] = name of class or tag(s)
359                        $styles[3] = : . or '' => followed by pseudo-element, class separator or nothing (empty)
360                        $styles[4] = name of pseudo-element after a tag, if any
361                        $styles[5] = the style definition itself, i.e. what's between the { }
362                */
363
364                // Now loop through the styles found and act accordingly;
365
366                // process TAG {...} & TAG1, TAG2,... {...} definitions only first by order of appearance
367                foreach ($styles[1] as $i => $type) {
368                        if ($type=="" && $styles[3][$i]=="") {
369                                $styledefinition = trim($styles[5][$i]);
370                                $styletag = preg_replace("/ *, */", "|", trim($styles[2][$i])); //echo $styletag."<br />";
371                                $styleclass = "";
372                                // process TAG {...} and TAG1, TAG2 {...} but not TAG1 TAG2 {...} or #divname styles
373                                if (!preg_match("/ /", $styletag) && !preg_match("/#/", $styletag)) {
374                                        $pattern = "!<(".$styletag.")([^>]*(?= /)|[^>]*)( /)?>!mi";
375                                        $this->Body = preg_replace_callback ($pattern, 'facteur_addstyle' , $this->Body);
376                                        $styles[6][$i]=1; // mark as injected inline
377                                }
378                        }
379                }
380
381                // append additional .CLASS {...} and TAG.CLASS {...} styling by order of appearance
382                // important to do so after TAG {...} definitions, so that class attributes override TAG styles when needed
383                foreach ($styles[1] as $i => $type) {
384                        if ($type!="." && $styles[3][$i]=="." ) {       // class definition for a specific tag
385                                $styledefinition = trim($styles[5][$i]);
386                                $styletag = trim($styles[2][$i]);
387                                $styleclass = trim($styles[4][$i]);
388                                $pattern = "!<(".$styletag.")([^>]* class\=['\"][^'\"]*".$styleclass."[^'\"]*['\"][^>]*(?= /)|[^>]* class\=['\"][^'\"]*".$styleclass."[^'\"]*['\"][^>]*)( />)?>!mi";
389                                $this->Body = preg_replace_callback ($pattern, 'facteur_addstyle' , $this->Body);
390                                $styles[6][$i]=1; // mark as injected inline
391
392                        }
393                        elseif ($type=="." && $styles[3][$i]=="" ) {    // general class definition for any tag
394                                $styledefinition = trim($styles[5][$i]);
395                                $styletag = "";
396                                $styleclass = trim($styles[2][$i]);
397                                $pattern = "!<(\w+)([^>]* class\=['\"]".$styleclass."['\"][^>]*(?= /)|[^>]* class\=['\"]".$styleclass."['\"][^>]*)( />)?>!mi";
398                                $this->Body = preg_replace_callback ($pattern, 'facteur_addstyle' , $this->Body);
399                                $styles[6][$i]=1; // mark as injected inline
400                        }
401                }
402
403
404                /* move all style declarations that weren't injected from <head> to a <body> <style> section,
405                         including but not limited to:
406                         - pseudo-classes like a:hover {...} as they can't be set inline
407                         - declaration chains like UL LI {...}
408                         - #divname {...}. These are not supported by email clients like Mac/Entourage anyway, it seems. */
409                foreach ($styles[1] as $i => $type) {
410                        if ($styles[6][$i]=="") {
411                                // add a <style type="text/css"> section after <body> if there's isn't one yet
412                                if (preg_match ("!<body[^>]*>\s*<style!mi", $this->Body)==0) {
413                                        $this->Body = preg_replace ("/(<body[^>]*>)/i", "\n\$1\n".'<style type="text/css">'."\n<!--\n-->\n</style>\n", $this->Body);
414                                }
415                                // append a copy of the pseudo-element declaration to that body style section
416                                $styledefinition = trim($styles[5][$i]);
417                                $styledefinition = preg_replace ("!\s+!mi", " ", $styledefinition ); // convert style definition to a one-liner (optional)
418                                $declaration = $styles[1][$i].trim($styles[2][$i]).$styles[3][$i].trim($styles[4][$i])." { ".$styledefinition." }";
419                                $this->Body = preg_replace ("!(<body[^>]*>\s*<style[^>]*>\s*<\!\-\-[^>]*)"."(\s*\-\->\s*</style>)!si", "\$1".$declaration."\n\$2", $this->Body);
420                                $styles[6][$i]= 2; // mark as moved to <style> section in <body>
421                        }
422                }
423
424                // remove stylesheet declaration(s) from <head> section (comment following line out if not wanted)
425                //$this->Body = preg_replace ("!(<head>.*)<style type.*</style>(.*</head>)!si", "\$1\$2" , $this->Body);
426
427                // check what styles have been injected
428#                       print_r($styles);
429
430        }
431
432
433        function safe_utf8_decode($text,$mode='texte_brut') {
434                if (!is_utf8($text))
435                        return ($text);
436
437                if (function_exists('iconv') && $mode == 'texte_brut') {
438                        $text = str_replace('’',"'",$text);
439                        $text = iconv("UTF-8", "ISO-8859-1//TRANSLIT", $text);
440                        return str_replace('&#8217;',"'",$text);
441                }
442                else {
443                        if ($mode == 'texte_brut') {
444                                $text = str_replace('’',"'",$text);
445                        }
446                        $text = unicode2charset(utf_8_to_unicode($text),'iso-8859-1');
447                        return str_replace('&#8217;',"'",$text);
448                }
449        }
450
451        function ConvertirUtf8VersIso8859() {
452                $this->CharSet  = 'iso-8859-1';
453                $this->Body             = str_replace('charset=utf-8', 'charset=iso-8859-1', $this->Body);
454                $this->Body             = $this->safe_utf8_decode($this->Body,'html');
455                $this->AltBody  = $this->safe_utf8_decode($this->AltBody);
456                $this->Subject  = $this->safe_utf8_decode($this->Subject);
457                $this->FromName = $this->safe_utf8_decode($this->FromName);
458        }
459
460        function ConvertirAccents() {
461                // tableau à compléter au fur et à mesure
462                $cor = array(
463                                                'à' => '&agrave;',
464                                                'â' => '&acirc;',
465                                                'ä' => '&auml;',
466                                                'ç' => '&ccedil;',
467                                                'é' => '&eacute;',
468                                                'è' => '&egrave;',
469                                                'ê' => '&ecirc;',
470                                                'ë' => '&euml;',
471                                                'î' => '&icirc;',
472                                                'ï' => '&iuml;',
473                                                'ò' => '&ograve;',
474                                                'ô' => '&ocirc;',
475                                                'ö' => '&ouml;',
476                                                'ù' => '&ugrave;',
477                                                'û' => '&ucirc;',
478                                                'œ' => '&oelig;',
479                                                '€' => '&euro;'
480                                        );
481
482                $this->Body = strtr($this->Body, $cor);
483        }
484
485}
486
487?>
Note: See TracBrowser for help on using the repository browser.