![]() |
Xataface Email Module 0.2
Email/Mailmerge Module for Xataface
|
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 <info@weblite.ca>'; 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 (>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 ?>