Xataface Email Module 0.2
Email/Mailmerge Module for Xataface
actions/email.php
Go to the documentation of this file.
00001 <?php
00002 /*-------------------------------------------------------------------------------
00003  * Xataface Web Application Framework
00004  * Copyright (C) 2005-2008 Web Lite Solutions Corp (shannah@sfu.ca)
00005  * 
00006  * This program is free software; you can redistribute it and/or
00007  * modify it under the terms of the GNU General Public License
00008  * as published by the Free Software Foundation; either version 2
00009  * of the License, or (at your option) any later version.
00010  * 
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  * 
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software
00018  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
00019  *-------------------------------------------------------------------------------
00020  */
00021 
00022 
00033 class actions_email {
00034 
00035         var $messages = array();
00036         var $emailTable;
00037         var $joinTable;
00038         var $recipientsTable;
00039         var $emailColumn;
00040 
00044         function handle(&$params){
00045                 $action =& $params['action'];
00046                 //print_r($params);
00047                 $app =& Dataface_Application::getInstance();
00048                 $query =& $app->getQuery();
00049                 $query['-skip'] = 0;
00050                 $query['-limit'] = 9999999;
00051                 
00052                 // Let's validate some of the parameters first
00053                 
00054                 // The actions.ini file should define an email_column and email_table parameters
00055                 // to indicate:
00056                 //      a. the name of the column from the current table that should be used
00057                 //          as the "send to" email address.
00058                 //      b. the name of the table that should store the email messages.
00059                 
00060                 
00061                 import('Dataface/Ontology.php');
00062                 Dataface_Ontology::registerType('Person', 'Dataface/Ontology/Person.php', 'Dataface_Ontology_Person');
00063                 $ontology =& Dataface_Ontology::newOntology('Person', $query['-table']);
00064                 $action['email_column'] = $ontology->getFieldname('email');
00065                 
00066                 
00067                 
00068                 if ( !@$action['email_column'] ) return PEAR::raiseError("No email column specified in actions.ini", DATAFACE_E_WARNING);
00069                 if ( !@$action['email_table'] ) return PEAR::raiseError("No email table specified in actions.ini", DATAFACE_E_WARNING);
00070                 
00071                 // Make sure the table and column names are not malicious.
00072                 $this->emailColumn = $col = $action['email_column'];
00073                 if ( strpos($col, '`') !== false ) return PEAR::raiseError("Invalid email column name: '$col'", DATAFACE_E_WARNING);
00074                 
00075                 $this->emailTable = $table = $action['email_table'];
00076                 if ( strpos($table, '`') !== false ) return PEAR::raiseError("Invalid email table name: '$table'", DATAFACE_E_WARNING);
00077                 
00078                 $this->joinTable = $join_table = 'xataface__email_log';//$query['-table'].'__email';
00079                 $join_table = $this->joinTable;
00080                 $this->recipientsTable = $query['-table'];
00081                         // The name of the table that tracks which records have had email sent.
00082                 
00083                 // Next make sure that the email table(s) exist(s)
00084                 if ( !Dataface_Table::tableExists($table, false) || !Dataface_Table::tableExists($join_table, false) ){
00085                         $this->createEmailTables($table, $join_table);
00086                 }
00087                 
00088                 $emailTableObj =& Dataface_Table::loadTable($this->emailTable);
00089                 $contentField =& $emailTableObj->getField('content');
00090                 $contentField['widget']['atts']['rows'] = 20;
00091                 $contentField['widget']['atts']['cols'] = 60;
00092                 $contentField['widget']['label'] = 'Message body';
00093                 $contentField['widget']['description'] = 'Please enter your message content in plain text.';
00094                 $contentField['widget']['type'] = 'ckeditor';
00095                 $contentField['widget']['ckeditor']['toolbar'] = 'XBasic';
00096                 $contentField['widget']['ckeditor']['extraPlugins'] = 'SchemaBrowser';
00097                 //$contentField['widget']['atts']['data-xf-schemabrowser-tablename'] = $query['-table'];
00098                 Dataface_JavascriptTool::getInstance()->import('xataface/modules/ckeditor/plugins/SchemaBrowser.js');
00099                 
00100                 //$contentField['widget']['editor'] = 'ckeditor';
00101                 
00102                 $subjectField =& $emailTableObj->getField('subject');
00103                 $subjectField['widget']['atts']['size'] = 60;
00104                 
00105                 $fromField =& $emailTableObj->getField('from');
00106                 $fromField['widget']['atts']['size'] = 60;
00107                 $fromField['widget']['description'] = 'e.g. Web Lite Solutions &lt;info@weblite.ca&gt;';
00108                 
00109                 
00110                 $ccField =& $emailTableObj->getField('cc');
00111                 $ccField['widget']['atts']['size'] = 60;
00112                 
00113                 $ignoreBlacklistField =& $emailTableObj->getField('ignore_blacklist');
00114                 $ignoreBlacklistField['widget']['type'] = 'checkbox';
00115                 $ignoreBlacklistField['widget']['description'] = 'The black list is a list of email addresses that have opted out of receiving email.  I.e. Users on the black list do not want to receive email.  Check this box if you want to send to blacklisted addresses despite their wish to be left alone.';
00116                 
00117                 
00118                 $templateField =& $emailTableObj->getField('template_id');
00119                 $templateField['widget']['filters']['table_name'] = $query['-table'];
00120                 
00121                 $form = df_create_new_record_form($table);
00122                 $form->_build();
00123                 
00124                 $form->addElement('hidden','-action');
00125                 $form->addElement('hidden','-table');
00126                 $form->setDefaults(array('-action'=>$query['-action'], '-table'=>$query['-table']));
00127                 $form->insertElementBefore($form->createElement('checkbox', 'send_now', '','Send now (leave this box unchecked if you wish these emails to be queued for later sending by the daily cron job.  Recommended to leave this box unchecked for large found sets (&gt;100 records).)'),'submit_new_newsletters_record');
00128                 $form->addElement('hidden', '-query_string');
00129                 $form->setDefaults(array('-query_string'=>base64_encode(serialize($query))));
00130                 $form->setSubmitLabel("Add to Mail Queue");
00131                 if ( @$app->_conf['from_email'] ){
00132                         $form->setDefaults(array('from'=>$app->_conf['from_email']));
00133                 }
00134                 
00135                 
00136 
00137                 if ( $form->validate() ){
00138                         $res = $form->process(array(&$form,'save'), true);
00139                         if ( PEAR::isError($res) ) return $res;
00140                         
00141                         // The form saved ok.. so we can send the emails.
00142                         $vals = $form->exportValues();
00143                         $q2 = unserialize(base64_decode($vals['-query_string']));
00144                         $qb = new Dataface_QueryBuilder($query['-table'], $q2);
00145                         $recTable = Dataface_Table::loadTable($query['-table']);
00146                         
00147                         $tkeys = $recTable->keys();
00148                         $keyCol = null;
00149                         foreach ($tkeys as $key=>$val){
00150                                 $keyCol = $key;
00151                                 break;
00152                         }
00153                         $sql = "insert ignore into `$join_table` (recipient_email,messageid,date_created,primary_key) select distinct (`".$col."`) `".$col."`, '".$form->_record->val('id')."' as messageid, now() as date_created, `".$keyCol."` as primary_key ".$qb->_from()." ".$qb->_secure($qb->_where());
00154                         
00155                         //echo $sql;exit;
00156                         $sres = df_q($sql);
00157                         
00158                         
00159                         //if ( !$sres ) trigger_error(mysql_error(df_db()), E_USER_ERROR);
00160                         $jobId = $this->postJob($form->_record->val('id'), $this->emailTable, $this->joinTable, $this->recipientsTable, $this->emailColumn);
00161                         $q2['-action'] = 'email_progress';
00162                         $q2['-job-id'] = $jobId;
00163                         unset($q2['-limit']);
00164                         header('Location: '.$app->url($q2).'&--msg='.urlencode("The message has been queued for delivery"));
00165                         exit;
00166                         
00167                 
00168                 }
00169                 
00170                 $addresses = array();
00171                 
00172                 ob_start();
00173                 $form->display();
00174                 $context = array();
00175                 $context['email_form'] = ob_get_contents();
00176                 $profileTable =& Dataface_Table::loadTable($query['-table']);
00177                 
00178                 $context['fields'] = array_keys($profileTable->fields(false,true,true));
00179                 $modurl = DATAFACE_SITE_URL.'/modules/Email';
00180                 if ( realpath(__FILE__) == realpath(DATAFACE_PATH.'/modules/Email/email.php') ){
00181                         $modurl = DATAFACE_URL.'/modules/Email';
00182                 }
00183                 
00184                 $context['EMAIL_ROOT'] = $modurl;
00185                 
00186                 ob_end_clean();
00187                 df_register_skin('email', dirname(__FILE__).'/../templates');
00188                 df_display($context, 'email_form.html');
00189                 
00190                 
00191         }
00192         
00193         function isBlackListed($email){
00194                 if ( !Dataface_Table::tableExists('dataface__email_blacklist') ) $this->createEmailTables(null,null);
00195                 $res = mysql_query("select email from dataface__email_blacklist where email='".addslashes($email)."' limit 1", df_db());
00196                 if ( !$res ) trigger_error(mysql_error(df_db()), E_USER_ERROR);
00197                 list($num) = mysql_fetch_row($res);
00198                 @mysql_free_result($res);
00199                 return $num;
00200         }
00201         
00202         function getBlackListed($emails){
00203                 if ( !Dataface_Table::tableExists('dataface__email_blacklist') ) $this->createEmailTables(null,null);
00204                 if ( !is_array($emails) ) $emails = array($emails);
00205                 $res = mysql_query("select email from dataface__email_blacklist where email in ('".implode("','", array_map('addslashes',$emails))."')", df_db());
00206                 $out = array();
00207                 if (!$res ) trigger_error(mysql_error(df_db()), E_USER_ERROR);
00208                 while ($row = mysql_fetch_row($res) ) $out[] = $row[0];
00209                 @mysql_free_result($res);
00210                 return $out;
00211         }
00212         
00221         function createEmailTables($tablename, $join_table){
00222                 $app =& Dataface_Application::getInstance();
00223                 
00224                 $sql = array();
00225                 if ( isset($tablename) ){
00226                         $sql[] = "create table if not exists `{$tablename}` (
00227                         `id` int(11) not null auto_increment,
00228                         `template_id` int(11) default null,
00229                         `subject` varchar(128) not null,
00230                         `cc` varchar(128) default null,
00231                         `from` varchar(128) default null,
00232                         `content` text,
00233                         `ignore_blacklist` tinyint(1) default 0,
00234                         posted_by varchar(255) default null,
00235                         primary key (`id`)) DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci";
00236                 }
00237                 if ( isset($join_table) ){
00238                         $sql[] = "create table if not exists `{$join_table}` (
00239                         `messageid` int(11) not null,
00240                         `recipient_email` varchar(128) not null,
00241                         `sent` tinyint(1) default 0,
00242                         `date_created` datetime default null,
00243                         `date_sent` datetime default null,
00244                         `success` tinyint(1) default null,
00245                         `comment` varchar(255) default null,
00246                         primary key (`messageid`,`recipient_email`))";
00247                 }
00248                 
00249                 
00250                 $sql[] = "create table if not exists `dataface__email_blacklist` (
00251                         `email` varchar(255) not null primary key
00252                         )";
00253                 
00254                 foreach ($sql as $q ){
00255                         $res = mysql_query($q, $app->db());
00256                         if ( !$res ) trigger_error(mysql_error($app->db()), E_USER_ERROR);
00257                 }
00258                 
00259                 try {
00260                         // The comment field was added in version 2.0... adding it now
00261                         // just in case there was a legacy table that we are adding to.
00262                         df_q("alter table `{$join_table}` add `comment` varchar(255) after `success`");
00263                 } catch (Exception $ex){}
00264                 return true;
00265                 
00266                 
00267         
00268         }
00269         
00270         
00271         private $_tempRecipientRecord;
00277         function _replaceFieldCallback($matches){
00278                 return $this->_tempRecipientRecord->display($matches[1]);
00279                 
00280         }
00281         
00282         
00291         function sendMail($emailId, $emailTable=null, $joinTable = null, $recipientsTable = null , $emailColumn = null){
00292                 require_once dirname(__FILE__).'/../lib/XPM/MIME.php';
00293                 if ( isset($emailTable) ) $this->emailTable = $emailTable;
00294                 if ( isset($joinTable) ) $this->joinTable = $joinTable;
00295                 if ( isset($recipientsTable) ) $this->recipientsTable = $recipientsTable;
00296                 if ( isset($emailColumn) ) $this->emailColumn = $emailColumn;
00297                 $app =& Dataface_Application::getInstance();
00298                 $conf =& $app->_conf;
00299                 
00300                 // We want to be able to override the replacement context
00301                 // via a delegate class method.  
00302                 $decorateEmailContextFunc = 'decorateEmailContext';
00303                 $recipientsTableObj = Dataface_Table::loadTable($this->recipientsTable);
00304                 $recipientsTableDelegate =& $recipientsTableObj->getDelegate();
00305                 $appDelegate =& $app->getDelegate();
00306                 $tableDecorateEmailContextFuncExists = (isset($recipientsTableDelegate) and method_exists($recipientsTableDelegate, $decorateEmailContextFunc) );
00307                 
00308                 $appDecorateEmailContextFuncExists = (isset($appDelegate) and method_exists($appDelegate, $decorateEmailContextFunc));
00309                 
00310                 
00311                 
00312                 if ( @$conf['_mail']['func'] ) $mail_func = $conf['_mail']['func'];
00313                 else $mail_func = 'mail';
00314                 
00315                 $emailTableObj =& Dataface_Table::loadTable($this->emailTable);
00316                 
00317                 $recTable = Dataface_Table::loadTable($this->recipientsTable);
00318                         
00319                 $tkeys = $recTable->keys();
00320                 $keyCol = null;
00321                 foreach ($tkeys as $key=>$val){
00322                         $keyCol = $key;
00323                         break;
00324                 }
00325                 
00326                 $emailTableObj->addRelationship('recipients', 
00327                         array('__sql__' => 'select * from `'.$this->recipientsTable.'` r inner join `'.$this->joinTable.'` j on (`r`.`'.$this->emailColumn.'` = j.recipient_email and `r`.`'.$keyCol.'`= j.primary_key) inner join `'.$this->emailTable.'` e on e.id = j.messageid where e.id=\''.addslashes($emailId).'\'')
00328                         );
00329                         
00330         
00331                 $email = df_get_record($this->emailTable, array('id'=>$emailId));
00332                 if ( !$email) return PEAR::raiseError("Failed to send email because no message with id {$emailId} could be found.", DATAFACE_E_ERROR);
00333                 
00334                 $jres = df_q("select job_id from xataface__email_jobs where email_id='".addslashes($emailId)."'");
00335                 if ( mysql_num_rows($jres) == 0 ){
00336                         throw new Exception("Could not find job associated with email.");
00337                 }
00338                 list($jobId) = mysql_fetch_row($jres);
00339                 @mysql_free_result($jres);
00340                 
00341                 $template = null;
00342                 if ( $email->val('template_id') ){
00343                         $template = df_get_record('xataface__email_templates', array('template_id'=>'='.$email->val('template_id')));
00344                 }
00345                 
00346                 // Let's update the count
00347                 $totalRecipients = $email->numRelatedRecords('recipients');
00348                 $res = df_q("update xataface__email_jobs set total_emails='".addslashes($totalRecipients)."' where email_id='".addslashes($emailId)."'");
00349                 
00350                 
00351                 $recipients = $email->getRelatedRecordObjects('recipients', 0,500, 'sent=0');
00352                 foreach ($recipients as $recipient ){
00353                         
00354                         
00355                         // Check to make sure that job hasn't been cancelled.
00356                         $jres = df_q("select cancelled from xataface__email_jobs where job_id='".addslashes($jobId)."'");
00357                         if ( mysql_num_rows($jres) == 0 ){
00358                                 throw new Exception("Could not find job record.  Must have been cancelled.");
00359                         }
00360                         list($cancelled) = mysql_fetch_row($jres);
00361                         @mysql_free_result($res);
00362                         if ( $cancelled ){
00363                                 return false;
00364                         }
00365                         sleep(5);
00366                         
00367                         
00368                         $recipientObj = $recipient->toRecord();
00369                         
00370                         
00371                         //$values = $recipient->strvals();
00372                         if ( $appDecorateEmailContextFuncExists ){
00373                                 $appDelegate->$decorateEmailContextFunc($email, $template,  $recipientObj);
00374                         }
00375                         
00376                         if ( $tableDecorateEmailContextFuncExists ){
00377                                 $recipientsTableDelegate->$decorateEmailContextFunc($email, $template, $recipientObj);
00378                         }
00379                         
00380                         
00381                         
00382                         
00383                         $keys = array();
00384                         //foreach ($values as $key=>$val) $keys[] = '/%'.$key.'%/';
00385                         //$values = array_values($values);
00386                         //$content = preg_replace($keys, $values, $recipient->strval('content'));
00387                         $this->_tempRecipientRecord = $recipientObj;
00388                         $content = preg_replace_callback('/\{\$([^\}]+)\}/', array($this, '_replaceFieldCallback'), $recipient->strval('content'));
00389                         
00390                         $uniqid = uniqid();
00391                         df_q("update xataface__email_log set uniqid='".addslashes($uniqid)."' where log_id='".addslashes($recipient->val('log_id'))."'");
00392                         
00393                         
00394                         $opt_out_url = df_absolute_url(DATAFACE_SITE_HREF.'?-action=email_opt_out&email='.urlencode($uniqid));
00395                         
00396                         $html_content = $content .= <<<END
00397                         <hr />
00398 <p>If you don't want to receive email updates from us, you can opt out of our mailing list by clicking <a href="$opt_out_url">here</a> .</p>
00399 END;
00400                         
00401                         $content .= <<<END
00402 
00403 ------------------------------------------------------------------
00404 If you don't want to receive email updates from us, you can opt out of our mailing list by going to $opt_out_url .
00405 END;
00406                         
00407                         $headers = array();
00408                         
00409                         if ( trim($email->strval('cc')) ){
00410                                 $headers[] = "Bcc: ".$email->strval('cc');
00411                         }
00412                         
00413                         if ( trim($email->strval('from')) ){
00414                                 $headers[] = "From: ".$email->strval('from');
00415                                 $headers[] = "Reply-to: ".$email->strval('from');
00416                         }
00417                         
00418                         if ( @$app->_conf['mail_host'] ){
00419                                 $headers[] = 'Message-ID: <' . md5(uniqid(time())) . '@'.$app->_conf['mail_host'].'>';
00420                         }
00421                         //$headers[] = "Content-Type: text/plain; charset=".$app->_conf['oe'];
00422                         
00423                         $joinRecord = $recipient->toRecord($this->joinTable);
00424                         
00425                         if ( !trim($recipient->val('recipient_email')) ){
00426                                 $joinRecord->setValue('success',0);
00427                                 $joinRecord->setValue('sent',1);
00428                                 $joinRecord->setValue('comment', 'Blank address');
00429                                 $joinRecord->save();
00430                                 unset($joinRecord);
00431                                 unset($recipient);
00432                                 continue;
00433                         }
00434                         
00435                         
00436                         // path to 'MIME.php' file from XPM4 package
00437                         
00438                         
00439                         // get ID value (random) for the embed image
00440                         $id = MIME::unique();
00441                         
00442                         // set text/plain version of message
00443                         $text = MIME::message(htmlspecialchars_decode(strip_tags(preg_replace(array('/<br[^>]*>/i','/<div[^>]*>/i','/<p[^>]*>/i', '/<table[^>]*>/i'), array("\r\n","\r\n","\r\n","\r\n"),$content))), 'text/plain', $app->_conf['oe'], MIME::HCHARSET);
00444                         // set text/html version of message
00445                         $html = MIME::message($html_content, 'text/html', $app->_conf['oe'], MIME::HCHARSET);
00446                         // add attachment with name 'file.txt'
00447                         //$at[] = MIME::message('source file', 'text/plain', 'file.txt', 'ISO-8859-1', 'base64', 'attachment');
00448                         //$file = 'xpertmailer.gif';
00449                         // add inline attachment '$file' with name 'XPM.gif' and ID '$id'
00450                         //$at[] = MIME::message(file_get_contents($file), FUNC::mime_type($file), 'XPM.gif', null, 'base64', 'inline', $id);
00451                         
00452                         // compose mail message in MIME format
00453                         $mess = MIME::compose($text, $html);
00454                         
00455                         $le = defined('PHP_EOL') ? PHP_EOL : "\n";
00456                         
00457                         
00458                         if ( !$email->val('ignore_blacklist') and $this->isBlackListed($recipient->val('recipient_email')) ){
00459                                 error_log("\nEmail address '".$recipient->val('recipient_email')."' is black listed so we do not send email to this address...");
00460                                 $joinRecord->setValue('success',0);
00461                                 $joinRecord->setValue('sent',1);
00462                                 $joinRecord->setValue('comment', 'Black listed');
00463                                 df_q("update xataface__email_jobs set sent_emails=sent_emails+1, blacklisted_emails=blacklisted_emails+1 where email_id='".addslashes($emailId)."'");
00464                                 
00465                         }
00466                         
00467                         else if ( $mail_func($recipient->strval('recipient_email'), $email->strval('subject'), $mess['content'], implode($le, $headers).$le.$mess['header']) ){
00468                                 $joinRecord->setValue('success',1);
00469                                 $joinRecord->setValue('sent',1);
00470                                 df_q("update xataface__email_jobs set sent_emails=sent_emails+1, successful_emails=successful_emails+1 where email_id='".addslashes($emailId)."'");
00471                                 //echo "Successfully sent email to ".$recipient->val('recipient_email');
00472                                 //echo "Successfully sent email to {$recipient->strval('recipient_email')}" ;
00473                                 //exit;
00474                         } else {
00475                                 $joinRecord->setValue('success',0);
00476                                 $joinRecord->setValue('sent',1);
00477                                 $this->messages[] = "Failed to send email to ".$email->val('recipient_email');
00478                                 error_log("Failed to send email to ".$email->val('recipient_email'));
00479                                 df_q("update xataface__email_jobs set sent_emails=sent_emails+1, failed_emails=failed_emails+1 where email_id='".addslashes($emailId)."'");
00480                                 //echo "Failed to send";
00481                                 //exit;
00482                         }
00483                         
00484                         $joinRecord->setValue('date_sent',date('Y-m-d H:i:s'));
00485                         $joinRecord->save();
00486                         
00487                         unset($joinRecord);
00488                         unset($recipient);
00489                         
00490                 
00491                 }
00492         
00493         }
00494         
00495         function postJob($emailId, $emailTable=null, $joinTable = null, $recipientsTable = null , $emailColumn = null){
00496 
00497                 $res = df_q("select count(*) from `$joinTable` where messageid='".addslashes($emailId)."'");
00498                 list($count) = mysql_fetch_row($res);
00499                 //echo "Posting job to join table: $joinTable with count ".$count;exit;
00500                 
00501                 $res = df_q(
00502                         "insert into xataface__email_jobs (
00503                                 email_id,
00504                                 email_table,
00505                                 join_table,
00506                                 recipients_table,
00507                                 email_column,
00508                                 active,
00509                                 total_emails,
00510                                 start_time
00511                                 )
00512                                 values (
00513                                 '".addslashes($emailId)."',
00514                                 '".addslashes($emailTable)."',
00515                                 '".addslashes($joinTable)."',
00516                                 '".addslashes($recipientsTable)."',
00517                                 '".addslashes($emailColumn)."',
00518                                 1,
00519                                 '".addslashes($count)."',
00520                                 '".time()."'
00521                                 )");
00522                 return mysql_insert_id(df_db());
00523                 
00524                 
00525         }
00526 }
00527 
00528 
00529 ?>
 All Data Structures Files Functions Variables Enumerations