source: spip-zone/_plugins_/facteur/branches/v2/facteur_fonctions.php @ 70525

Last change on this file since 70525 was 70384, checked in by cedric@…, 7 years ago

Quand le mail est vide en entree de facteur_email_wrap_to_html(), on renvoit du vide aussi, pour ne pas générer un mail HTML plein de vide (et ne pas fausser la detection de mail vide pour les nouveautés)

  • Property svn:executable set to *
File size: 11.3 KB
Line 
1<?php
2/*
3 * Plugin Facteur 2
4 * (c) 2009-2011 Collectif SPIP
5 * Distribue sous licence GPL
6 *
7 */
8
9if (!defined("_ECRIRE_INC_VERSION")) return;
10
11/**
12 * Transformer un mail texte ou HTML simplifie en mail HTML complet avec le wrapper emails/texte.html
13 * Si le mail est un mail texte :
14 *   la premiere ligne est le sujet
15 *   le reste est le corps du mail
16 *
17 * Si le mail est un mail HTML simplifie :
18 *   le sujet est entre <title></title>
19 *   le corps est entre <body></body>
20 *   une eventuelle intro peut etre fournie entre <intro></intro>
21 *
22 * @param string $texte_ou_html
23 * @return string
24 */
25function facteur_email_wrap_to_html($texte_ou_html){
26        $texte_ou_html = trim($texte_ou_html);
27        // attention : si pas de contenu on renvoi du vide aussi (mail vide = mail vide)
28        if (!strlen(trim($texte_ou_html)))
29                return $texte_ou_html;
30
31        $contexte = array("sujet"=>"","texte"=>"","intro"=>"");
32
33        // tester si le mail est en html (simplifie)
34        if (substr($texte_ou_html,0,1)=="<"
35          AND substr($texte_ou_html,-1,1)==">"
36          AND stripos($texte_ou_html,"</body>")!==false){
37
38                // dans ce cas on ruse un peu : extraire le sujet du title
39                $sujet = "";
40                if (preg_match(",<title>(.*)</title>,Uims",$texte_ou_html,$m)){
41                        $contexte['sujet'] = $m[1];
42                        $texte_ou_html = preg_replace(",<title>(.*)</title>,Uims","",$texte_ou_html,1);
43                        $texte_ou_html = trim($texte_ou_html);
44                }
45                if (preg_match(",<intro>(.*)</intro>,Uims",$texte_ou_html,$m)){
46                        $contexte['intro'] = $m[1];
47                        $texte_ou_html = preg_replace(",<intro>(.*)</intro>,Uims","",$texte_ou_html,1);
48                        $texte_ou_html = trim($texte_ou_html);
49                }
50                $contexte['html'] = preg_replace(",</?body>,ims","",$texte_ou_html);
51        }
52        else {
53                // la premiere ligne est toujours le sujet
54                $texte_ou_html = explode("\n",$texte_ou_html);
55                $contexte['sujet'] = trim(array_shift($texte_ou_html));
56                $contexte['texte'] = trim(implode("\n",$texte_ou_html));
57        }
58
59        // attention : si pas de contenu on renvoi du vide aussi (mail vide = mail vide)
60        if (!strlen(trim(implode("",$contexte))))
61                return "";
62
63        return recuperer_fond("emails/texte",$contexte);
64}
65
66        /*
67
68        Written by Eric Dols - edols@auditavenue.com
69
70        You may freely use or modify this, provided
71        you leave credits to the original coder.
72        Feedback about (un)successfull uses, bugs and improvements done
73        are much appreciated, but don't expect actual support.
74
75        PURPOSE OF THIS FUNCTION
76                It is designed to process html emails relying
77                on a css stylesheet placed in the <head> for layout in
78                order to enhance compatibility with email clients,
79                including webmail services.
80                Provided you use minimal css, you can keep styling separate
81                from the content in your email template, and let this function
82                "inject" those styles inline in your email html tags on-the-fly,
83                just before sending.
84                Technically, it grabs the style declarations found in the
85                <head> section and inserts each declaration inline,
86                inside the corresponding html tags in the email message.
87
88                Supports both HTML and XHTML markup seamlessly. Thus
89                tolerant to email message writers using non-xhtml tag,
90                even when template is xhtml compliant (e.g. they would
91                add <img ...> instead of a xhtml compliant <img ... />).
92
93        NEW 10 dec. 2003:
94                - code revised, including a few regexp bugs fixed.
95                - multiple class for a tag are now allowed <p class="firstclass secondclass">
96                - all unsupported css styles are now moved to the body section (not just a:hover etc...)
97
98        USE
99                Add this function to a function library include, like "inline.inc"
100                and include it near the beginning of your php page:
101                require ("inline.inc");
102
103                load the html source of message into a variable
104                like $html_source and process it using:
105                $html_source = sheet2inline($html_source)
106
107
108        STYLE DEFINITIONS SUPPORTED
109                TAG { ... }
110                TAG1, TAG2, ... { ... }
111                TAG.class { ... }
112                .class { ...)
113                TAG:pseudo { ... }
114
115
116                CSS definitions may be freely formatted (spaces, tabs, linefeeds...),
117                they are converted to oneliners before inserting them inline in the html tags.
118
119                .class definitions are processed AFTER tag definitions,
120                thus appended inline after any existing tag styling to
121                preserve the normal css priority behavior.
122
123                Existing style="..." attributes in tags are NOT stripped. However they MUST
124                be with double quotes. If not, an addtional style="..." attribute will be added
125
126
127        KNOWN LIMITATIONS
128                - style info should be placed in <head> section. I believe
129                        it shouldnt be too hard to modify to point to an external
130                        stylesheet instead.
131                - no support (yet?):
132                        * chains like P UL LI { .... } or P UL LI.class { .... }
133                        * #divname p { ... } and <tag id="...">
134                        * a:hover, a:visited {...} multiple class:pseudo
135                        They require a significantly more complicated processing likely
136                        based on stylesheet and document trees parsing.
137                        Many email clients don't handle more than what is supported
138                        by this script anyway.
139                - pseudo-classes like a:hover {...} can't be inserted inline
140                        in the html tags: they are moved to a <style> declaration in
141                        the <body> instead. This is a limitation from html, not this script.
142                - It is still up to you to check if target email clients render
143                        your css styled templates correctly, especially webmail services
144                        like Hotmail, in which the email becomes a sub-part of an html page,
145                        with styles already in place.
146        */
147function facteur_convertir_styles_inline($body){
148        // variables to be accessed in the callback sub-function too
149        global $styledefinition, $styletag, $styleclass;
150
151        // Let's first load the stylesheet information in a $styles array using a regexp
152        preg_match_all ( "/^[ \t]*([.]?)([\w, #]+)([.:])?(\S*)\s+{([^}]+)}/mi", $body , $styles);
153        /*
154                $styles[1] = . or ''  => .class or tag (empty)
155                $styles[2] = name of class or tag(s)
156                $styles[3] = : . or '' => followed by pseudo-element, class separator or nothing (empty)
157                $styles[4] = name of pseudo-element after a tag, if any
158                $styles[5] = the style definition itself, i.e. what's between the { }
159        */
160
161        // Now loop through the styles found and act accordingly;
162
163        // process TAG {...} & TAG1, TAG2,... {...} definitions only first by order of appearance
164        foreach ($styles[1] as $i => $type) {
165                if ($type=="" && $styles[3][$i]=="") {
166                        $styledefinition = trim($styles[5][$i]);
167                        $styletag = preg_replace("/ *, */", "|", trim($styles[2][$i])); //echo $styletag."<br />";
168                        $styleclass = "";
169                        // process TAG {...} and TAG1, TAG2 {...} but not TAG1 TAG2 {...} or #divname styles
170                        if (!preg_match("/ /", $styletag) && !preg_match("/#/", $styletag)) {
171                                $pattern = "!<(".$styletag.")([^>]*(?= /)|[^>]*)( /)?>!mi";
172                                $body = preg_replace_callback ($pattern, 'facteur_addstyle' , $body);
173                                $styles[6][$i]=1; // mark as injected inline
174                        }
175                }
176        }
177
178        // append additional .CLASS {...} and TAG.CLASS {...} styling by order of appearance
179        // important to do so after TAG {...} definitions, so that class attributes override TAG styles when needed
180        foreach ($styles[1] as $i => $type) {
181                if ($type!="." && $styles[3][$i]=="." ) {       // class definition for a specific tag
182                        $styledefinition = trim($styles[5][$i]);
183                        $styletag = trim($styles[2][$i]);
184                        $styleclass = trim($styles[4][$i]);
185                        $pattern = "!<(".$styletag.")([^>]* class\=['\"][^'\"]*".$styleclass."[^'\"]*['\"][^>]*(?= /)|[^>]* class\=['\"][^'\"]*".$styleclass."[^'\"]*['\"][^>]*)( />)?>!mi";
186                        $body = preg_replace_callback ($pattern, 'facteur_addstyle' , $body);
187                        $styles[6][$i]=1; // mark as injected inline
188
189                }
190                elseif ($type=="." && $styles[3][$i]=="" ) {    // general class definition for any tag
191                        $styledefinition = trim($styles[5][$i]);
192                        $styletag = "";
193                        $styleclass = trim($styles[2][$i]);
194                        $pattern = "!<(\w+)([^>]* class\=['\"]".$styleclass."['\"][^>]*(?= /)|[^>]* class\=['\"]".$styleclass."['\"][^>]*)( />)?>!mi";
195                        $body = preg_replace_callback ($pattern, 'facteur_addstyle' , $body);
196                        $styles[6][$i]=1; // mark as injected inline
197                }
198        }
199
200
201        /* move all style declarations that weren't injected from <head> to a <body> <style> section,
202                 including but not limited to:
203                 - pseudo-classes like a:hover {...} as they can't be set inline
204                 - declaration chains like UL LI {...}
205                 - #divname {...}. These are not supported by email clients like Mac/Entourage anyway, it seems. */
206        foreach ($styles[1] as $i => $type) {
207                if ($styles[6][$i]=="") {
208                        // add a <style type="text/css"> section after <body> if there's isn't one yet
209                        if (preg_match ("!<body[^>]*>\s*<style!mi", $body)==0) {
210                                $body = preg_replace ("/(<body[^>]*>)/i", "\n\$1\n".'<style type="text/css">'."\n<!--\n-->\n</style>\n", $body);
211                        }
212                        // append a copy of the pseudo-element declaration to that body style section
213                        $styledefinition = trim($styles[5][$i]);
214                        $styledefinition = preg_replace ("!\s+!mi", " ", $styledefinition ); // convert style definition to a one-liner (optional)
215                        $declaration = $styles[1][$i].trim($styles[2][$i]).$styles[3][$i].trim($styles[4][$i])." { ".$styledefinition." }";
216                        $body = preg_replace ("!(<body[^>]*>\s*<style[^>]*>\s*<\!\-\-[^>]*)"."(\s*\-\->\s*</style>)!si", "\$1".$declaration."\n\$2", $body);
217                        $styles[6][$i]= 2; // mark as moved to <style> section in <body>
218                }
219        }
220
221        // remove stylesheet declaration(s) from <head> section (comment following line out if not wanted)
222        //$body = preg_replace ("!(<head>.*)<style type.*</style>(.*</head>)!si", "\$1\$2" , $body);
223
224        // check what styles have been injected
225#                       print_r($styles);
226
227        return $body;
228}
229
230/**
231 * facteur_addstyle
232 * @author Eric Dols
233 *
234 * @param $matches
235 * @return string
236 */
237function facteur_addstyle($matches) {
238
239        // $matches[1]=tag, $matches[2]=tag attributes (if any), $matches[3]=xhtml closing (if any)
240
241        // variables values set in calling function
242        global $styledefinition, $styletag, $styleclass;
243
244        // convert the style definition to a one-liner
245        $styledefinition = preg_replace ("!\s+!mi", " ", $styledefinition );
246        // convert all double-quotes to single-quotes
247        $styledefinition = preg_replace ('/"/','\'', $styledefinition );
248
249        if (preg_match ("/style\=/i", $matches[2])) {
250                        // add styles to existing style attribute if any already in the tag
251                        $pattern = "!(.* style\=)[\"]([^\"]*)[\"](.*)!mi";
252                        $replacement = "\$1".'"'."\$2 ".$styledefinition.'"'."\$3";
253                        $attributes = preg_replace ($pattern, $replacement , $matches[2]);
254        } else {
255                        // otherwise add new style attribute to tag (none was present)
256                        $attributes = $matches[2].' style="'.$styledefinition.'"';
257        }
258
259        if ($styleclass!="") {
260                // if we were injecting a class style, remove the now useless class attribute from the html tag
261
262                // Single class in tag case (class="classname"): remove class attribute altogether
263                $pattern = "!(.*) class\=['\"]".$styleclass."['\"](.*)!mi";
264                $replacement = "\$1\$2";
265                $attributes = preg_replace ( $pattern, $replacement, $attributes);
266
267                // Multiple classes in tag case (class="classname anotherclass..."): remove class name from class attribute.
268                // classes are injected inline and removed by order of appearance in <head> stylesheet
269                // exact same behavior as where last declared class attributes in <style> take over (IE6 tested only)
270                $pattern = "!(.* class\=['\"][^\"]*)(".$styleclass." | ".$styleclass.")([^\"]*['\"].*)!mi";
271                $replacement = "\$1\$3";
272                $attributes = preg_replace ( $pattern, $replacement, $attributes);
273
274        }
275
276        return "<".$matches[1].$attributes.$matches[3].">";
277}
278
279/**
280 * Un filtre pour transformer les retour ligne texte en br si besoin (si pas autobr actif)
281 *
282 * @param string $texte
283 * @return string
284 */
285function facteur_nl2br_si_pas_autobr($texte){
286        return (_AUTOBR?$texte:nl2br($texte));
287}
288
289?>
Note: See TracBrowser for help on using the repository browser.