CMS  Version 3.9
email_manager.inc
Go to the documentation of this file.
1 <?php
7 /**************************************************************
8 
9 Copyright (c) 2007,2008 Sonjara, Inc
10 
11 Permission is hereby granted, free of charge, to any person
12 obtaining a copy of this software and associated documentation
13 files (the "Software"), to deal in the Software without
14 restriction, including without limitation the rights to use,
15 copy, modify, merge, publish, distribute, sublicense, and/or sell
16 copies of the Software, and to permit persons to whom the
17 Software is furnished to do so, subject to the following
18 conditions:
19 
20 The above copyright notice and this permission notice shall be
21 included in all copies or substantial portions of the Software.
22 
23 Except as contained in this notice, the name(s) of the above
24 copyright holders shall not be used in advertising or otherwise
25 to promote the sale, use or other dealings in this Software
26 without prior written authorization.
27 
28 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
29 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
30 OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
31 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
32 HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
33 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
34 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
35 OTHER DEALINGS IN THE SOFTWARE.
36 
37 *****************************************************************/
38 
39 /*
40  * Title: email_manager.inc
41  *
42  * Description: Class for constructing email messages from
43  * a template and sending emails.
44  *
45  * author: Janice Gallant for Sonjara, Inc.
46  *
47  * date: 5/13/10
48  *
49  ***************************************************************/
50 
51 Fakoli::using("settings");
52 
53 abstract class AbstractEmailTransport
54 {
55  var $to = null;
56  var $subject = "No Subject";
57  var $htmlMessage = "";
58  var $plainMessage = "";
59  var $emailFrom = "";
60  var $emailName = "";
61  var $replyTo = "";
62  var $replyToName = "";
63  var $returnPath = "";
64  var $attachments = array();
65  var $ical = null;
66  var $icalMethod = null;
67 
68  function setTo($to) { $this->to = $to; }
69  function setSubject($subject) { $this->subject = $subject; }
70  function setHTMLMessage($message) { $this->htmlMessage = $message; }
72  function setFrom($email, $name = "") { $this->emailFrom = $email; $this->emailName = $name; }
73  function setReplyTo($email, $name) { $this->replyTo = $email; $this->replyToName = $name; }
74  function setReturnPath($path) { $this->returnPath = $path; }
75  function addAttachment($filename, $attachment) { $this->attachments[$filename] = $attachment; }
76  function setICalAttachment($ical, $method) { $this->ical = $ical; $this->icalMethod = $method; }
77  abstract function send();
78 }
79 
81 {
82  function __construct()
83  {
84  }
85 
86  function send()
87  {
88  $site_email_from = Settings::getValue('email', 'email_from');
89 
90  $returnPath = $this->returnPath ? $this->returnPath : $this->emailFrom;
91  $replyTo = $this->replyTo ? "{$this->replyName} <{$this->replyTo}>" : "{$this->emailName} <{$this->emailFrom}>";
92  // Use the MD5 algorithm to generate a random hash
93  $from = $this->emailName . " <" . $this->emailFrom . ">";
94 
95  $headers = "Return-Path: ". $returnPath . "\r\n";
96  $headers .= "Reply-To: ". $replyTo ."\r\n";
97  $headers .= "MIME-Version: 1.0\r\n";
98 
99  $random_hash = md5(date('r', time()));
100 
101  $headers .= "Content-Type: multipart/alternative; boundary=\"--PHP-alt-".$random_hash."\"\r\n";
102 
103  $message = "";
104 
105  if (!$this->plainMessage) $this->plainMessage = HTMLToText($this->htmlMessage);
106 
107  $message .= <<<ENDMESSAGE
108 ----PHP-alt-{$random_hash}
109 Content-Type: text/plain
110 Content-Disposition: inline
111 Content-Transfer-Encoding: 8bit
112 
113 {$this->plainMessage}
114 
115 
116 ----PHP-alt-{$random_hash}
117 Content-Type: text/html
118 Content-Disposition: inline
119 Content-Transfer-Encoding: 8bit
120 
121 {$this->htmlMessage}
122 ENDMESSAGE;
123 
124  foreach($this->attachments as $filename => $attachment)
125  {
126  $message .= "----PHP-alt-{$random_hash}\r\n";
128  }
129 
130  if ($this->ical)
131  {
132  $message .= <<<ENDMESSAGE
133 ----PHP-alt-{$random_hash}
134 Content-Type: text/calendar; charset=utf-8;method={$this->icalMethod}
135 Content-Disposition: inline; filename=meeting.ics
136 Content-Transfer-Encoding: 8bit
137 
138 ENDMESSAGE;
139  }
140 
141  trace("Sending Mail from {$from} to {$this->to}: {$this->subject}", 3);
142  $this->mail = new Mail($site_email_from, $this->to, $this->subject, $message, $headers, $from);
143 
144  $rtn = $this->mail->send();
145 
146  return $rtn;
147  }
148 
150  {
151  $fileAttachments = "";
152  if(is_file($attachment))
153  {
154  $base_filename = basename($attachment);
155 
156  $fileAttachments .= "----PHP-alt-{$random_hash}\n";
157  $fp = @fopen($attachment, "rb");
158  $fileContents = @fread($fp, filesize($attachment));
159  @fclose($fp);
160  $fileContents = chunk_split(base64_encode($fileContents));
161  $fileAttachments .= "Content-Type: application/octet-stream; name=\"". $base_filename ."\"\n" .
162  "Content-Description: ".$base_filename."\n" .
163  "Content-Disposition: attachment;\n" . " filename=\"". $base_filename ."\"; size=".filesize($attachment).";\n" .
164  "Content-Transfer-Encoding: base64\n\n" . $fileContents . "\n\n";
165  }
166 
167  return $fileAttachments;
168  }
169 }
170 
172 {
174  var $onSendComplete; // optional user callback function after mail is sent
175 
188  function EmailHandler($toAddr, $subject, $message = "", $emailFrom = "", $emailName = "", $attachments = array(), $iCalMgr = null)
189  {
190  global $config;
191 
192  $site_email_from = Settings::getValue('email', 'email_from');
193  $site_email_name = Settings::getValue('email', 'email_name');
194 
195  $replyTo = $site_email_from;
196  $replyToName = $site_email_name;
197 
198  if($emailFrom)
199  {
200  $replyTo = $emailFrom;
201  }
202 
203  if($emailName)
204  {
205  $replyToName = $emailName;
206  }
207 
208  $toAddr = trim($toAddr);
209 
210  if ($config['html_email_template'])
211  {
212  $file = $config['homedir'] . $config['html_email_template'];
213  trace("Loading email template '$file'", 3);
214  $template = file_get_contents($file);
215  $template = str_replace("{var:styles}", Fakoli::getStyles(), $template);
216  $message = str_replace("{message}", $message, $template);
217  }
218 
219  $this->transport = EmailManager::createTransport();
220 
221  $this->transport->setTo($toAddr);
222  $this->transport->setFrom($site_email_from, $site_email_name);
223  $this->transport->setReplyTo($replyTo, $replyToName);
224  $this->transport->setReturnPAth($site_email_from);
225  $this->transport->setSubject($subject);
226  $this->transport->setHTMLMessage($message);
227  $this->transport->setPlainMessage(HTMLToText($message));
228 
229  if($attachments && !is_array($attachments))
230  {
231  $attachments = array_combine(basename($attachments), $attachments);
232  }
233 
234  foreach($attachments as $filename => $attachment)
235  {
236  $this->transport->addAttachment($filename, $attachment);
237  }
238 
239  if($iCalMgr)
240  {
241  $iCalMgr->setOrganizer($replyTo, $replyToName);
242  $iCalMgr->setAttendee($toAddr);
243 
244  $this->transport->setICalAttachment($iCalMgr->format(), $iCalMgr->data->get("method"));
245  }
246 
247  }
248 
249  /*
250  * This isn't working on zither; works locally - zither can't determine
251  * which view to send and sends both but shows the html as text with tags
252  */
253  function getMessageScript($msg, $random_hash, $attachments, $iCalMgr)
254  {
255  global $config;
256 
257 
258  $fileAttachments = $this->formatAttachments($attachments, $random_hash);
259 
260  if($iCalMgr)
261  {
262  $iCal = $this->formatiCalendar($iCalMgr, $random_hash);
263  }
264 
265  //define the body of the message.
266  ob_start(); //Turn on output buffering
267  ?>
268 
269 ----PHP-alt-<?php echo "$random_hash\n"; ?>
270 Content-Type: text/plain
271 Content-Disposition: inline
272 Content-Transfer-Encoding: 8bit
273 
274 <?php echo trim(HTMLToText($msg)); ?>
275 
276 
277 ----PHP-alt-<?php echo "$random_hash\n"; ?>
278 Content-Type: text/html
279 Content-Disposition: inline
280 Content-Transfer-Encoding: 8bit
281 
282 <?php echo $html ?>
283 
284 <?php echo $fileAttachments ?>
285 
286 <?php echo $iCal ?>
287 
288 ----PHP-alt-<?php echo $random_hash; ?>--
289  <?php
290  //copy current buffer contents into $message variable
291  // and delete current output buffer
292  $message = ob_get_clean();
293 
294  return $message;
295  }
296 
302  function formatAttachments($attachments, $random_hash)
303  {
304  if(!count($attachments)) return "";
305 
306  foreach($attachments as $display_name => $attachment)
307  {
308  if(is_file($attachment))
309  {
310  $base_filename = basename($attachment);
311 
312  $fileAttachments .= "----PHP-alt-{$random_hash}\n";
313  $fp = @fopen($attachment, "rb");
314  $fileContents = @fread($fp, filesize($attachment));
315  @fclose($fp);
316  $fileContents = chunk_split(base64_encode($fileContents));
317  $fileAttachments .= "Content-Type: application/octet-stream; name=\"". $base_filename ."\"\n" .
318  "Content-Description: ".$base_filename."\n" .
319  "Content-Disposition: attachment;\n" . " filename=\"".$display_name ."\"; size=".filesize($attachment).";\n" .
320  "Content-Transfer-Encoding: base64\n\n" .$fileContents . "\n\n";
321  }
322  }
323 
324  return $fileAttachments;
325  }
326 
334  function formatiCalendar($iCalMgr, $random_hash)
335  {
336  $method = $iCalMgr->data->get("method");
337 
338  $cal_message = "----PHP-alt-{$random_hash}\n";
339  $cal_message .= "Content-Type: text/calendar; charset=utf-8;method={$method}\n";
340  $cal_message .= "Content-Disposition: inline; filename=meeting.ics\n";
341  $cal_message .= "Content-Transfer-Encoding: 8bit\n";
342 
343  $cal_message .= $iCalMgr->format();
344 
345  return $cal_message;
346  }
347 
348  function send()
349  {
350  $rtn = $this->transport->send();
351 
352  trace("EmailHandler send rtn $rtn", 3);
353  // Call the callback whether successful or not; calling script
354  // decides how to handle either way.
355  if ($this->onSendComplete AND is_callable($this->onSendComplete))
356  {
357  call_user_func($this->onSendComplete, $rtn);
358  }
359  return $rtn;
360  }
361 }
362 
363 
394 {
395  static $transports = null;
396 
402  static function registerTransport($mode, $class)
403  {
405  {
406  EmailManager::$transports = array();
407  }
408 
410  }
411 
417  static function createTransport($mode = null)
418  {
419  if (EmailManager::$transports == null)
420  {
421  ComponentManager::fireEvent("RegisterEmailTransport");
422  }
423 
424  if (!$mode)
425  {
426  $mode = Settings::getValue("email", "transport_mode");
427  }
428 
430  return new $class;
431  }
432 
433  static function getTransportModes()
434  {
435  if (EmailManager::$transports == null)
436  {
437  ComponentManager::fireEvent("RegisterEmailTransport");
438  }
439 
440  return array_keys(EmailManager::$transports);
441  }
442 
443  static function registerSMTPTransport()
444  {
445  EmailManager::registerTransport("SMTP", 'SMTPEmailTransport');
446  }
447 
449  {
451  }
452 
453  static function settingsExtension($form)
454  {
456  $options = array_combine($transports, $transports);
457  $form->dropdown("transport_mode", "Selected Transport Mode", $options);
458  }
459 
461  var $item; // dataitem object of the base class sending the emails
463  var $message;
464  var $subject;
468  var $onSendComplete; // optional user-defined callback function after send is successful
470 
472  {
473  $this->item = clone($item);
474  $this->item->filter = null;
475  $this->emailTemplate = $emailTemplate;
476  $this->emailTemplateName = ($this->item) ? $this->item->format($emailTemplate->name) : $emailTemplate->name;
477  $this->emailFrom = ($this->item) ? $this->item->format($emailTemplate->sender_email) : $emailTemplate->sender_email;
478  $this->mergeEmail();
479  $this->onSendComplete = $onSendComplete;
480  }
481 
486  private function cleanupTemplateOutput($output)
487  {
488  trace("** Cleaning Up Email Template Output HTML", 3);
489 
490  $output = preg_replace("/<p>\s*<\!DOCTYPE/s", "<!DOCTYPE", $output);
491  $output = preg_replace("/<p>\s*<html/s", "<html", $output);
492  $output = preg_replace("/<\\/html>\\s*<\\/p>/s", "</html>", $output);
493  $baseURL = Settings::getValue("email", "HTML_email_base_url");
494  trace("Base URL: $baseURL", 3);
495 
496  if ($baseURL)
497  {
498  $output = str_replace("<head>", "<head><base href='{$baseURL}'>", $output);
499  }
500 
501  return $output;
502  }
503 
508  function mergeEmail()
509  {
510  $emailText = $this->emailTemplate->recipients . $this->emailTemplate->subject . $this->emailTemplate->message;
511  $mgr = new MergeCodeManager($this->item, $emailText, $this->emailTemplate->class_name);
512 
513  $fields = array("recipients", "subject", "message");
514  foreach($fields as $field)
515  $this->$field = $mgr->searchAndReplace($this->emailTemplate->$field);
516 
517  $this->message = $this->cleanupTemplateOutput($this->message);
518  }
519 
520  function getRecipients()
521  {
522  $validRecipients = array();
523 
524  trace("EmailManager:getRecipients {$this->recipients}", 3);
525  if($this->recipients)
526  {
527  $recipients = explode(",", $this->recipients);
528  if(count($recipients) > 0)
529  {
530  foreach($recipients as $recipient)
531  {
532  if(preg_match('/\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}\b/i', $recipient))
533  $validRecipients[] = $recipient;
534  else
535  trace("EmailManager:getRecipients invalid recipient $recipient", 3);
536  }
537  }
538  }
539 
540  return $validRecipients;
541  }
542 
543  /*
544  * Call the callback whether successful or not; calling script
545  * decides how to handle either way. Each recipient could have
546  * different result so we don't record multiple recipients in
547  * one record.
548  */
549 
550  function sendEmail()
551  {
552  $recipients = $this->getRecipients();
553  $rtn = false;
554 
555  if(count($recipients) > 0)
556  {
557  foreach($recipients as $recipient)
558  {
559  $rtn = $this->sendOneEmail($recipient);
560  }
561  }
562  else
563  {
564  trace("EmailManager:: Warning - no valid recipients", 3);
565  }
566  return $rtn;
567  }
568 
569  function sendOneEmail($recipient)
570  {
571  trace("EmailManager:: sending email to recipient $recipient", 3);
572 
573  $email = new EmailHandler($recipient, $this->subject, $this->message, $this->emailFrom, $this->emailName, $this->attachments);
574 
575  $rtn = $email->send();
576 
577  if ($this->onSendComplete AND is_callable($this->onSendComplete))
578  {
579  call_user_func($this->onSendComplete, $this, $recipient, $rtn);
580  }
581 
582  EmailLog::logEmail($this, $recipient, $rtn);
583  return $rtn;
584  }
585 
593  static function send($source, $name, $onSendComplete = "")
594  {
596 
597  if($emailTemplate)
598  {
599  if (is_array($source))
600  {
601  $sendingItem = new JoinResult();
602  foreach($source as $item)
603  {
604  $cl = get_class($item);
605  $sendingItem->$cl = $item;
606  }
607  }
608  else
609  {
610  $sendingItem = $source;
611  }
612 
613  trace("Sending email '$name'", 3);
614  $emailManager = new EmailManager($sendingItem, $emailTemplate, $onSendComplete);
615  $rtn = $emailManager->sendEmail();
616  trace("EmailManager:: rtn code $rtn", 3);
617  }
618  else
619  {
620  $rtn = 0;
621  trace("EmailManager::no template found", 3);
622  }
623  return $rtn;
624  }
625 
626  static function setDefaults()
627  {
628  trace("EmailManager::setDefaults", 3);
629 
630  global $config;
631 
632  Settings::setDefaultValue("email", "transport_mode", "SMTP", "String", "Specifies the mail transport for system generated emails", "Mail Transport", null, 0);
633 
634  Settings::setDefaultValue("email", "use_debugging_mode", false, "Boolean", "Enable local logging of generated emails (instead of sending via SMTP)", "Debugging Mode", null, 0);
635 
636  $defaultPath = $config['uploadbase'] . DIRECTORY_SEPARATOR . "email_debug_logs";
637 
638  if (!file_exists($defaultPath)) mkdir($defaultPath);
639 
640  Settings::setDefaultValue("email", "debugging_mode_output_path", $defaultPath, "String", "Folder that will store the logged emails in debugging mode", "Debugging Mode", null, 1);
641  Settings::setDefaultValue("email", "email_from", "andy@sonjara.com", "String", "Default From address for generated emails", "Contact Details", null, 1);
642  Settings::setDefaultValue("email", "email_contact", "andy@sonjara.com", "String", "Default To address for generated emails", "Contact Details", null, 2);
643  Settings::setDefaultValue("email", "email_name", "Andy Green", "String", "Default Name of email contact for generated emails", "Contact Details", null, 3);
644  Settings::setDefaultValue("email", "default_contact_us_recipient", "", "String", "Default email address(es) that will receive messages from the Contact Us form. These are used if no Contact Topics are defined", "Contact Details", null, 4);
645  Settings::setDefaultValue("email", "use_captcha", "Never", String, "Specify when to use a Captcha to verify the user", "Contact Details", "Never\nAnonymous Only\nAlways", 5);
646  Settings::setDefaultValue("email", "HTML_email_base_url", "", String, "", "Contact Details", null, 6);
647 
648  Settings::setDefaultValue("email", "override_PHP_settings", false, Boolean, "Use these server settings instead of the global settings in your php.ini file", "Server Settings", 0);
649  Settings::setDefaultValue("email", "mail_server", "localhost", "String", "Host name or IP address of your upstream SMTP server", "Server Settings", "", 1);
650  Settings::setDefaultValue("email", "mail_server_port", 25, Number, "Port to use on the upstream SMTP server", "Server Settings", "", 2);
651  Settings::setDefaultValue("email", "username", "", String, "SMTP Account username (if authentication is required)", "Server Settings", "", 3);
652  Settings::setDefaultValue("email", "password", "", String, "SMTP Account password (if authentication is required)", "Server Settings", "", 4);
653 
654  Settings::setDefaultValue("email", "log_messages", false, Boolean, "Log messages in the database after sending", "Message Logging", "", 1);
655  Settings::setDefaultValue("email", "log_message_bodies", false, Boolean, "Specifies whether to store the message text when logging an email message. ".
656  "It is recommended to leave this unchecked to conserve space in the database.", "Message Logging", "", 2);
657  }
658 
659  static function upgradeComponent($version)
660  {
661  $mgr = new EmailUpgradeManager();
662  $mgr->upgrade($version);
663  }
664 
665  /*
666  * Scan the email debug log path to retrieve
667  * all the log files.
668  */
669  static function scanDebugLogs()
670  {
671  $path = Settings::getValue("email", "debugging_mode_output_path");
672 
673  $logs = array();
674 
675  trace("EmailManager::Scanning $path", 3);
676 
677  $handle = opendir($path);
678 
679  if(!$handle)
680  return;
681 
682  $idx = 0;
683  while(false !== ($file = readdir($handle)))
684  {
685  // omit "." and ".." in the directory
686  if (!preg_match("/(^\.{1,2}$)/i", $file))
687  {
688  $log = new EmailDebugLog();
689  $log->log_id = $idx;
690  $log->filename = $file;
691  $log->date = date("F d, Y g:ia", (filemtime($path . DIRECTORY_SEPARATOR . $file)));
692  $logs[$log->log_id] = $log;
693  $idx++;
694  }
695  }
696 
697  closedir($handle);
698 
699  return $logs;
700  }
701 
703  {
704  SerializationManager::registerHandler("email_templates", "Email Templates", new SimpleSerializationHandler(EmailTemplate));
706  return true;
707  }
708 }
709 
710 
711 /*
712  * $sendindItem - object of a DataModel class. The templates class_name
713  * must be the same class as the object
714  *
715  * $name - name of the email template to be sent
716  *
717  * $onSendComplete - optional user callback function, e.g., record
718  * email to log file
719  */
720 function sendEmailUsingEmailManager($sendingItem, $name, $onSendComplete = "")
721 {
723 
724  if($emailTemplate)
725  {
726  trace("Sending email '$name'", 3);
727  $emailManager = new EmailManager($sendingItem, $emailTemplate, $onSendComplete);
728  $rtn = $emailManager->sendEmail();
729  trace("EmailManager:: rtn code $rtn", 3);
730  }
731 
732  return $rtn;
733 }
734 ?>
$form
if(! $page) $path
Definition: page.inc:57
if(! $attachment_id) $attachment
Definition: delete.inc:42
$file
Definition: delete.inc:47
$name
Definition: upload.inc:54
$attachment filename
Definition: upload.inc:87
setFrom($email, $name="")
setReplyTo($email, $name)
setICalAttachment($ical, $method)
addAttachment($filename, $attachment)
static fireEvent($event, $parameter=null, $mustBeConsumed=false)
Fire an event to all subscribers as detailed in their manifests.
formatiCalendar($iCalMgr, $random_hash)
The iCal event should come in formatted by the event handler formatICal function.
formatAttachments($attachments, $random_hash)
EmailHandler($toAddr, $subject, $message="", $emailFrom="", $emailName="", $attachments=array(), $iCalMgr=null)
getMessageScript($msg, $random_hash, $attachments, $iCalMgr)
static logEmail($mgr, $toAddr, $rtn)
Record email sent in a log.
Definition: email_log.inc:31
Takes an email template and an obj of any DataItem class and sends email to a list of recipients afte...
static scanDebugLogs()
mergeEmail()
Use MergeCodeManager, which shares functionality with TextLookup.
static getTransportModes()
static upgradeComponent($version)
sendOneEmail($recipient)
static registerSettingsFormExtension()
$emailName
The name of the email sender (optional)
static setDefaults()
static settingsExtension($form)
$emailTemplateName
The name of the email template.
static send($source, $name, $onSendComplete="")
Sends an email template, based on the supplied DataItem (or array of DataItems)
static registerSerializationHandler()
static registerSMTPTransport()
EmailManager($item, $emailTemplate, $onSendComplete="")
static createTransport($mode=null)
$attachments
optional - array of files to attach to the email
static registerTransport($mode, $class)
Registers an email transport handler.
static $transports
static getEmailTemplate($name)
Retrieves the named email templated.
static getStyles()
Returns the HTML link tags for CSS files specified by the registered components in their manifest fil...
Definition: core.inc:603
static using()
Import the datamodels, views and manifest for the specified component(s).
Definition: core.inc:116
Definition: mail.inc:40
writeAttachment($filename, $attachment)
registerHandler($component, $title, $handler)
Registers a serialization handler for a component.
static getValue($component, $name)
Retrieve the value of the specified Setting.
Definition: settings.inc:104
static setDefaultValue($component, $name, $value, $field_type="String", $annotation="", $category="", $options="", $weight=0)
Sets the default value of the given component setting.
Definition: settings.inc:174
static registerExtension($component, $handler)
Provides a simple implementation of a SerializationHandler that can serialize a single DataItem class...
$method
Pull out a simple reference to the request method.
Definition: core.inc:1573
$forumRequest to
$output
Definition: generate.inc:9
sendEmailUsingEmailManager($sendingItem, $name, $onSendComplete="")
$mode
global $config
Definition: import.inc:4
$email recipients
Definition: mail_to.inc:53
$message
Definition: mail_to.inc:49
$msg
Definition: save.inc:10
$email subject
$email message
if(!Settings::getValue("debug", "enable_trace_file_downloads")) $filename
Definition: trace.inc:42