]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - modules/EmailTemplates/EmailTemplate.php
Release 6.3.1
[Github/sugarcrm.git] / modules / EmailTemplates / EmailTemplate.php
1 <?php
2 if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
3 /*********************************************************************************
4  * SugarCRM Community Edition is a customer relationship management program developed by
5  * SugarCRM, Inc. Copyright (C) 2004-2011 SugarCRM Inc.
6  * 
7  * This program is free software; you can redistribute it and/or modify it under
8  * the terms of the GNU Affero General Public License version 3 as published by the
9  * Free Software Foundation with the addition of the following permission added
10  * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
11  * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
12  * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
13  * 
14  * This program is distributed in the hope that it will be useful, but WITHOUT
15  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16  * FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
17  * details.
18  * 
19  * You should have received a copy of the GNU Affero General Public License along with
20  * this program; if not, see http://www.gnu.org/licenses or write to the Free
21  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22  * 02110-1301 USA.
23  * 
24  * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
25  * SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
26  * 
27  * The interactive user interfaces in modified source and object code versions
28  * of this program must display Appropriate Legal Notices, as required under
29  * Section 5 of the GNU Affero General Public License version 3.
30  * 
31  * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
32  * these Appropriate Legal Notices must retain the display of the "Powered by
33  * SugarCRM" logo. If the display of the logo is not reasonably feasible for
34  * technical reasons, the Appropriate Legal Notices must display the words
35  * "Powered by SugarCRM".
36  ********************************************************************************/
37
38 /*********************************************************************************
39
40  * Description:  TODO: To be written.
41  * Portions created by SugarCRM are Copyright (C) SugarCRM, Inc.
42  * All Rights Reserved.
43  * Contributor(s): ______________________________________..
44  ********************************************************************************/
45
46
47
48
49
50
51 // EmailTemplate is used to store email email_template information.
52 class EmailTemplate extends SugarBean {
53         var $field_name_map = array();
54         // Stored fields
55         var $id;
56         var $date_entered;
57         var $date_modified;
58         var $modified_user_id;
59         var $created_by;
60         var $created_by_name;
61         var $modified_by_name;
62         var $name;
63         var $published;
64         var $description;
65         var $body;
66         var $body_html;
67     var $subject;
68         var $attachments;
69         var $from_name;
70         var $from_address;
71         var $table_name = "email_templates";
72         var $object_name = "EmailTemplate";
73         var $module_dir = "EmailTemplates";
74         var $new_schema = true;
75         // This is used to retrieve related fields from form posts.
76         var $additional_column_fields = array(
77         );
78         // add fields here that would not make sense in an email template
79         var $badFields = array(
80                 'account_description',
81                 'contact_id',
82                 'lead_id',
83                 'opportunity_amount',
84                 'opportunity_id',
85                 'opportunity_name',
86                 'opportunity_role_id',
87                 'opportunity_role_fields',
88                 'opportunity_role',
89                 'campaign_id',
90                 // User objects
91                 'id',
92                 'date_entered',
93                 'date_modified',
94                 'user_preferences',
95                 'accept_status',
96                 'user_hash',
97                 'authenticate_id',
98                 'sugar_login',
99                 'reports_to_id',
100                 'reports_to_name',
101                 'is_admin',
102                 'receive_notifications',
103                 'modified_user_id',
104                 'modified_by_name',
105                 'created_by',
106                 'created_by_name',
107                 'accept_status_id',
108                 'accept_status_name',
109         );
110
111         function EmailTemplate() {
112                 parent::SugarBean();
113         }
114
115         /**
116          * Generates the extended field_defs for creating macros
117          * @param object $bean SugarBean
118          * @param string $prefix "contact_", "user_" etc.
119          * @return
120          */
121         function generateFieldDefsJS() {
122                 global $current_user;
123
124
125
126
127
128                 $contact = new Contact();
129                 $account = new Account();
130                 $lead = new Lead();
131                 $prospect = new Prospect();
132
133
134                 $loopControl = array(
135                         'Contacts' => array(
136                             'Contacts' => $contact,
137                             'Leads' => $lead,
138                                 'Prospects' => $prospect,
139                         ),
140                         'Accounts' => array(
141                                 'Accounts' => $account,
142                         ),
143                         'Users' => array(
144                                 'Users' => $current_user,
145                         ),
146                 );
147
148                 $prefixes = array(
149                         'Contacts' => 'contact_',
150                         'Accounts' => 'account_',
151                         'Users' => 'contact_user_',
152                 );
153
154                 $collection = array();
155                 foreach($loopControl as $collectionKey => $beans) {
156                         $collection[$collectionKey] = array();
157                         foreach($beans as $beankey => $bean) {
158
159                                 foreach($bean->field_defs as $key => $field_def) {
160                                     if( ($field_def['type'] == 'relate' && empty($field_def['custom_type'])) ||
161                                                 ($field_def['type'] == 'assigned_user_name' || $field_def['type'] =='link') ||
162                                                 ($field_def['type'] == 'bool') ||
163                                                 (in_array($field_def['name'], $this->badFields)) ) {
164                                         continue;
165                                     }
166                                     if(!isset($field_def['vname'])) {
167                                         //echo $key;
168                                     }
169                                     // valid def found, process
170                                     $optionKey = strtolower("{$prefixes[$collectionKey]}{$key}");
171                                     $optionLabel = preg_replace('/:$/', "", translate($field_def['vname'], $beankey));
172                                     $dup=1;
173                                     foreach ($collection[$collectionKey] as $value){
174                                         if($value['name']==$optionKey){
175                                                 $dup=0;
176                                                 break;
177                                         }
178                                     }
179                                     if($dup)
180                                         $collection[$collectionKey][] = array("name" => $optionKey, "value" => $optionLabel);
181                                 }
182                         }
183                 }
184
185                 $json = getJSONobj();
186                 $ret = "var field_defs = ";
187                 $ret .= $json->encode($collection, false);
188                 $ret .= ";";
189                 return $ret;
190         }
191
192         function get_summary_text() {
193                 return "$this->name";
194         }
195
196         function create_export_query(&$order_by, &$where) {
197                 return $this->create_new_list_query($order_by, $where);
198         }
199
200         function fill_in_additional_list_fields() {
201                 $this->fill_in_additional_parent_fields();
202         }
203
204         function fill_in_additional_detail_fields() {
205             if (empty($this->body) && !empty($this->body_html))
206         {
207             $this->body = strip_tags(html_entity_decode($this->body_html));
208         }
209                 $this->created_by_name = get_assigned_user_name($this->created_by);
210                 $this->modified_by_name = get_assigned_user_name($this->modified_user_id);
211                 $this->fill_in_additional_parent_fields();
212         }
213
214         function fill_in_additional_parent_fields() {
215         }
216
217         function get_list_view_data() {
218                 global $app_list_strings, $focus, $action, $currentModule;
219                 $fields = $this->get_list_view_array();
220                 $fields["DATE_MODIFIED"] = substr($fields["DATE_MODIFIED"], 0 , 10);
221                 return $fields;
222         }
223
224         //function all string that match the pattern {.} , also catches the list of found strings.
225         //the cache will get refreshed when the template bean instance changes.
226         //The found url key patterns are replaced with name value pairs provided as function parameter. $tracked_urls.
227         //$url_template is used to construct the url for the email message. the template should have place holder for 1 varaible parameter, represented by %1
228         //$template_text_array is a list of text strings that need to be searched. usually the subject, html body and text body of the email message.
229         //$removeme_url_template, if the url has is_optout property checked then use this template.
230         function parse_tracker_urls($template_text_array,$url_template,$tracked_urls,$removeme_url_template) {
231                 global $beanFiles,$beanList, $app_list_strings,$sugar_config;
232                 if (!isset($this->parsed_urls))
233                         $this->parsed_urls=array();
234
235                 //parse the template and find all the dynamic strings that need replacement.
236                 $pattern = '/\{*[^\{\}]*\}/'; // cn: bug 6638, find multibyte strings
237                 foreach ($template_text_array as $key=>$template_text) {
238                         if (!empty($template_text)) {
239                 if(!isset($this->parsed_urls[$key]) || $this->parsed_urls[$key]['text'] != $template_text) {
240                                         $matches=array();
241                                         $count=preg_match_all($pattern,$template_text,$matches,PREG_OFFSET_CAPTURE);
242                                         $this->parsed_urls[$key]=array('matches' => $matches, 'text' => $template_text);
243                                 } else {
244                                         $matches=$this->parsed_urls[$key]['matches'];
245                                         if(!empty($matches[0])) {
246                                                 $count=count($matches[0]);
247                                         } else {
248                                                 $count=0;
249                                         }
250                                 }
251
252                                 //navigate thru all the matched keys and replace the keys with actual strings.
253                                 for ($i=($count -1); $i>=0; $i--) {
254                                         $url_key_name=$matches[0][$i][0];
255
256                                         if (!empty($tracked_urls[$url_key_name])) {
257                                                 if ($tracked_urls[$url_key_name]['is_optout']==1){
258                                                         $tracker_url = $removeme_url_template;
259                                                 } else {
260                                                         $tracker_url = sprintf($url_template,$tracked_urls[$url_key_name]['id']);
261                                                 }
262                                         }
263                                         if(!empty($tracker_url) && !empty($template_text) && !empty($matches[0][$i][0]) && !empty($tracked_urls[$matches[0][$i][0]])){
264                         $template_text=substr_replace($template_text,$tracker_url,$matches[0][$i][1], strlen($matches[0][$i][0]));
265                         $template_text=str_replace($sugar_config['site_url'].'/'.$sugar_config['site_url'],$sugar_config['site_url'],$template_text);
266                     }
267                                 }
268                         }
269                         $return_array[$key]=$template_text;
270                 }
271
272                 return $return_array;
273         }
274
275         function parse_email_template($template_text_array, $focus_name, $focus, &$macro_nv) {
276
277
278                 global $beanFiles, $beanList, $app_list_strings;
279
280                 // generate User instance that owns this "Contact" for contact_user_* macros
281                 $user = new User();
282         if(isset($focus->assigned_user_id)  && !empty($focus->assigned_user_id)){
283                   $user->retrieve($focus->assigned_user_id);
284         }
285
286                 if(!isset($this->parsed_entities))
287                         $this->parsed_entities=array();
288
289                 //parse the template and find all the dynamic strings that need replacement.
290         // Bug #48111 It's strange why prefix for User module is contact_user (see self::generateFieldDefsJS method)
291         if ($beanList[$focus_name] == 'User')
292         {
293             $pattern_prefix = '$contact_user_';
294         }
295         else
296         {
297             $pattern_prefix = '$'.strtolower($beanList[$focus_name]).'_';
298         }
299                 $pattern_prefix_length = strlen($pattern_prefix);
300                 $pattern = '/\\'.$pattern_prefix.'[A-Za-z_0-9]*/';
301
302                 foreach($template_text_array as $key=>$template_text) {
303                         if(!isset($this->parsed_entities[$key])) {
304                                 $matches = array();
305                                 $count = preg_match_all($pattern, $template_text, $matches, PREG_OFFSET_CAPTURE);
306
307                                 if($count != 0) {
308                                         for($i=($count -1); $i>=0; $i--) {
309                                                 if(!isset($matches[0][$i][2])) {
310                                                         //find the field name in the bean.
311                                                         $matches[0][$i][2]=substr($matches[0][$i][0],$pattern_prefix_length,strlen($matches[0][$i][0]) - $pattern_prefix_length);
312
313                                                         //store the localized strings if the field is of type enum..
314                                                         if(isset($focus->field_defs[$matches[0][$i][2]]) && $focus->field_defs[$matches[0][$i][2]]['type']=='enum' && isset($focus->field_defs[$matches[0][$i][2]]['options'])) {
315                                                                 $matches[0][$i][3]=$focus->field_defs[$matches[0][$i][2]]['options'];
316                                                         }
317                                                 }
318                                         }
319                                 }
320                                 $this->parsed_entities[$key]=$matches;
321                         } else {
322                                 $matches=$this->parsed_entities[$key];
323                                 if(!empty($matches[0])) {
324                                         $count=count($matches[0]);
325                                 } else {
326                                         $count=0;
327                                 }
328                         }
329
330                         for ($i=($count -1); $i>=0; $i--) {
331                                 $field_name=$matches[0][$i][2];
332
333                                 // cn: feel for User object attribute key and assign as found
334                                 if(strpos($field_name, "user_") === 0) {
335                                         $userFieldName = substr($field_name, 5);
336                                         $value = $user->$userFieldName;
337                                         //_pp($userFieldName."[{$value}]");
338                                 } else {
339                                         $value = $focus->{$field_name};
340                                 }
341
342                                 //check dom
343                                 if(isset($matches[0][$i][3])) {
344                                         if(isset($app_list_strings[$matches[0][$i][3]][$value])) {
345                                                 $value=$app_list_strings[$matches[0][$i][3]][$value];
346                                         }
347                                 }
348
349                 //generate name value pair array of macros and corresponding values for the targed.
350                 $macro_nv[$matches[0][$i][0]] =$value;
351
352                                 $template_text=substr_replace($template_text,$value,$matches[0][$i][1], strlen($matches[0][$i][0]));
353                         }
354
355                         //parse the template for tracker url strings. patter for these strings in {[a-zA-Z_0-9]+}
356
357                         $return_array[$key]=$template_text;
358                 }
359
360                 return $return_array;
361         }
362
363
364         /**
365          * Convenience method to parse for user's values in a template
366          * @param array $repl_arr
367          * @param object $user
368          * @return array
369          */
370         function _parseUserValues($repl_arr, &$user) {
371                 foreach($user->field_defs as $field_def) {
372                         if(($field_def['type'] == 'relate' && empty($field_def['custom_type'])) || $field_def['type'] == 'assigned_user_name') {
373                         continue;
374                         }
375
376                         if($field_def['type'] == 'enum') {
377                                 $translated = translate($field_def['options'], 'Users', $user->$field_def['name']);
378
379                                 if(isset($translated) && ! is_array($translated)) {
380                                         $repl_arr["contact_user_".$field_def['name']] = $translated;
381                                 } else { // unset enum field, make sure we have a match string to replace with ""
382                                         $repl_arr["contact_user_".$field_def['name']] = '';
383                                 }
384                         } else {
385                                 if(isset($user->$field_def['name'])) {
386                                         $repl_arr["contact_user_".$field_def['name']] = $user->$field_def['name'];
387                                 } else {
388                                         $repl_arr["contact_user_".$field_def['name']] = "";
389                                 }
390                         }
391                 }
392
393                 return $repl_arr;
394         }
395
396
397         function parse_template_bean($string, $bean_name, &$focus) {
398                 global $current_user;
399                 global $beanFiles, $beanList;
400                 $repl_arr = array();
401
402                 // cn: bug 9277 - create a replace array with empty strings to blank-out invalid vars
403                 if(!class_exists('Account'))
404                 if(!class_exists('Contact'))
405                 if(!class_exists('Leads'))
406                 if(!class_exists('Prospects'))
407                 
408                 require_once('modules/Accounts/Account.php');
409                 $acct = new Account();
410                 $contact = new Contact();
411                 $lead = new Lead();
412                 $prospect = new Prospect();
413                 
414                 foreach($lead->field_defs as $field_def) {
415                         if(($field_def['type'] == 'relate' && empty($field_def['custom_type'])) || $field_def['type'] == 'assigned_user_name') {
416                         continue;
417                         }
418                         $repl_arr["contact_".$field_def['name']] = '';
419                         $repl_arr["contact_account_".$field_def['name']] = '';
420                 }
421                 foreach($prospect->field_defs as $field_def) {
422                         if(($field_def['type'] == 'relate' && empty($field_def['custom_type'])) || $field_def['type'] == 'assigned_user_name') {
423                         continue;
424                         }
425                         $repl_arr["contact_".$field_def['name']] = '';
426                         $repl_arr["contact_account_".$field_def['name']] = '';
427                 }
428                 foreach($contact->field_defs as $field_def) {
429                         if(($field_def['type'] == 'relate' && empty($field_def['custom_type'])) || $field_def['type'] == 'assigned_user_name') {
430                         continue;
431                         }
432                         $repl_arr["contact_".$field_def['name']] = '';
433                         $repl_arr["contact_account_".$field_def['name']] = '';
434                 }
435                 foreach($acct->field_defs as $field_def) {
436                         if(($field_def['type'] == 'relate' && empty($field_def['custom_type'])) || $field_def['type'] == 'assigned_user_name') {
437                         continue;
438                         }
439                         $repl_arr["account_".$field_def['name']] = '';
440                         $repl_arr["account_contact_".$field_def['name']] = '';
441                 }
442                 // cn: end bug 9277 fix
443
444
445                 // feel for Parent account, only for Contacts traditionally, but written for future expansion
446                 if(isset($focus->account_id) && !empty($focus->account_id)) {
447                         $acct->retrieve($focus->account_id);
448                 }
449
450                 if($bean_name == 'Contacts') {
451                         // cn: bug 9277 - email templates not loading account/opp info for templates
452                         if(!empty($acct->id)) {
453                                 foreach($acct->field_defs as $field_def) {
454                                         if(($field_def['type'] == 'relate' && empty($field_def['custom_type'])) || $field_def['type'] == 'assigned_user_name') {
455                                 continue;
456                                         }
457
458                                         if($field_def['type'] == 'enum') {
459                                                 $translated = translate($field_def['options'], 'Accounts' ,$acct->$field_def['name']);
460
461                                                 if(isset($translated) && ! is_array($translated)) {
462                                                         $repl_arr["account_".$field_def['name']] = $translated;
463                                                         $repl_arr["contact_account_".$field_def['name']] = $translated;
464                                                 } else { // unset enum field, make sure we have a match string to replace with ""
465                                                         $repl_arr["account_".$field_def['name']] = '';
466                                                         $repl_arr["contact_account_".$field_def['name']] = '';
467                                                 }
468                                         } else {
469                                                 $repl_arr["account_".$field_def['name']] = $acct->$field_def['name'];
470                                                 $repl_arr["contact_account_".$field_def['name']] = $acct->$field_def['name'];
471                                         }
472                                 }
473                         }
474
475                         if(!empty($focus->assigned_user_id)) {
476                                 $user = new User();
477                                 $user->retrieve($focus->assigned_user_id);
478                                 $repl_arr = EmailTemplate::_parseUserValues($repl_arr, $user);
479                         }
480                 } elseif($bean_name == 'Users') {
481                         /**
482                          * This section of code will on do work when a blank Contact, Lead,
483                          * etc. is passed in to parse the contact_* vars.  At this point,
484                          * $current_user will be used to fill in the blanks.
485                          */
486                         $repl_arr = EmailTemplate::_parseUserValues($repl_arr, $current_user);
487                 } else {
488                         // assumed we have an Account in focus
489                         foreach($contact->field_defs as $field_def) {
490                                 if(($field_def['type'] == 'relate' && empty($field_def['custom_type'])) || $field_def['type'] == 'assigned_user_name' || $field_def['type'] == 'link') {
491                         continue;
492                                 }
493
494                                 if($field_def['type'] == 'enum') {
495                                         $translated = translate($field_def['options'], 'Accounts' ,$contact->$field_def['name']);
496
497                                         if(isset($translated) && ! is_array($translated)) {
498                                                 $repl_arr["contact_".$field_def['name']] = $translated;
499                                                 $repl_arr["contact_account_".$field_def['name']] = $translated;
500                                         } else { // unset enum field, make sure we have a match string to replace with ""
501                                                 $repl_arr["contact_".$field_def['name']] = '';
502                                                 $repl_arr["contact_account_".$field_def['name']] = '';
503                                         }
504                                 } else {
505                                         if (isset($contact->$field_def['name'])) {
506                                                 $repl_arr["contact_".$field_def['name']] = $contact->$field_def['name'];
507                                                 $repl_arr["contact_account_".$field_def['name']] = $contact->$field_def['name'];
508                                         } // if
509                                 }
510                         }
511                 }
512
513                 ///////////////////////////////////////////////////////////////////////
514                 ////    LOAD FOCUS DATA INTO REPL_ARR
515                 foreach($focus->field_defs as $field_def) {
516                         if(isset($focus->$field_def['name'])) {
517                                 if(($field_def['type'] == 'relate' && empty($field_def['custom_type'])) || $field_def['type'] == 'assigned_user_name') {
518                         continue;
519                                 }
520
521                                 if($field_def['type'] == 'enum' && isset($field_def['options'])) {
522                                         $translated = translate($field_def['options'],$bean_name,$focus->$field_def['name']);
523
524                                         if(isset($translated) && ! is_array($translated)) {
525                                                 $repl_arr[strtolower($beanList[$bean_name])."_".$field_def['name']] = $translated;
526                                         } else { // unset enum field, make sure we have a match string to replace with ""
527                                                 $repl_arr[strtolower($beanList[$bean_name])."_".$field_def['name']] = '';
528                                         }
529                                 } else {
530                                         $repl_arr[strtolower($beanList[$bean_name])."_".$field_def['name']] = $focus->$field_def['name'];
531                                 }
532                         } else {
533                                 if($field_def['name'] == 'full_name') {
534                                         $repl_arr[strtolower($beanList[$bean_name]).'_full_name'] = $focus->get_summary_text();
535                                 } else {
536                                         $repl_arr[strtolower($beanList[$bean_name])."_".$field_def['name']] = '';
537                                 }
538                         }
539                 } // end foreach()
540
541                 krsort($repl_arr);
542                 reset($repl_arr);
543                 //20595 add nl2br() to respect the multi-lines formatting
544                 if(isset($repl_arr['contact_primary_address_street'])){
545                     $repl_arr['contact_primary_address_street'] = nl2br($repl_arr['contact_primary_address_street']);
546                 }
547                 if(isset($repl_arr['contact_alt_address_street'])){
548                     $repl_arr['contact_alt_address_street'] = nl2br($repl_arr['contact_alt_address_street']);   
549                 }
550
551                 foreach ($repl_arr as $name=>$value) {
552                         if($value != '' && is_string($value)) {
553                                 $string = str_replace("\$$name", $value, $string);
554                         } else {
555                                 $string = str_replace("\$$name", ' ', $string);
556                         }
557                 }
558
559                 return $string;
560         }
561
562         function parse_template($string, &$bean_arr) {
563                 global $beanFiles, $beanList;
564
565                 foreach($bean_arr as $bean_name => $bean_id) {
566                         require_once($beanFiles[$beanList[$bean_name]]);
567
568                         $focus = new $beanList[$bean_name];
569                         $result = $focus->retrieve($bean_id);
570
571                         if($bean_name == 'Leads' || $bean_name == 'Prospects') {
572                                 $bean_name = 'Contacts';
573                         }
574
575                         if(isset($this) && isset($this->module_dir) && $this->module_dir == 'EmailTemplates') {
576                                 $string = $this->parse_template_bean($string, $bean_name, $focus);
577                         } else {
578                                 $string = EmailTemplate::parse_template_bean($string, $bean_name, $focus);
579                         }
580                 }
581                 return $string;
582         }
583
584         function bean_implements($interface) {
585                 switch($interface) {
586                         case 'ACL':return true;
587                 }
588                 return false;
589         }
590
591         function is_used_by_email_marketing() {
592                 $query = "select id from email_marketing where template_id='$this->id' and deleted=0";
593                 $result = $this->db->query($query);
594                 if($this->db->fetchByAssoc($result)) {
595                         return true;
596                 }
597                 return false;
598         }
599 }
600 ?>