3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.4 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2013 |
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
10 | CiviCRM is free software; you can copy, modify, and distribute it |
11 | under the terms of the GNU Affero General Public License |
12 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
14 | CiviCRM is distributed in the hope that it will be useful, but |
15 | WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
17 | See the GNU Affero General Public License for more details. |
19 | You should have received a copy of the GNU Affero General Public |
20 | License and the CiviCRM Licensing Exception along |
21 | with this program; if not, contact CiviCRM LLC |
22 | at info[AT]civicrm[DOT]org. If you have questions about the |
23 | GNU Affero General Public License or the licensing of CiviCRM, |
24 | see the CiviCRM license FAQ at http://civicrm.org/licensing |
25 +--------------------------------------------------------------------+
31 * @copyright CiviCRM LLC (c) 2004-2013
37 * This file is used to build the form configuring mailing details
39 class CRM_Mailing_Form_Upload
extends CRM_Core_Form
{
42 function preProcess() {
43 $this->_mailingID
= $this->get('mailing_id');
44 if (CRM_Core_Permission
::check('administer CiviCRM')) {
45 $this->assign('isAdmin', 1);
48 //when user come from search context.
49 $ssID = $this->get('ssID');
50 $this->assign('ssid',$ssID);
51 $this->_searchBasedMailing
= CRM_Contact_Form_Search
::isSearchContext($this->get('context'));
52 if(CRM_Contact_Form_Search
::isSearchContext($this->get('context')) && !$ssID){
54 $result = CRM_Core_BAO_PrevNextCache
::getSelectedContacts();
55 $this->assign("value", $result);
59 * This function sets the default values for the form.
60 * the default values are retrieved from the database
66 function setDefaultValues() {
67 $mailingID = CRM_Utils_Request
::retrieve('mid', 'Integer', $this, FALSE, NULL);
69 //need to differentiate new/reuse mailing, CRM-2873
70 $reuseMailing = FALSE;
75 $mailingID = $this->_mailingID
;
78 $count = $this->get('count');
79 $this->assign('count', $count);
81 $this->set('skipTextFile', FALSE);
82 $this->set('skipHtmlFile', FALSE);
88 $dao = new CRM_Mailing_DAO_Mailing();
89 $dao->id
= $mailingID;
91 $dao->storeValues($dao, $defaults);
93 //we don't want to retrieve template details once it is
95 $templateId = $this->get('template');
96 $this->assign('templateSelected', $templateId ?
$templateId : 0);
97 if (isset($defaults['msg_template_id']) && !$templateId) {
98 $defaults['template'] = $defaults['msg_template_id'];
99 $messageTemplate = new CRM_Core_DAO_MessageTemplate();
100 $messageTemplate->id
= $defaults['msg_template_id'];
101 $messageTemplate->selectAdd();
102 $messageTemplate->selectAdd('msg_text, msg_html');
103 $messageTemplate->find(TRUE);
105 $defaults['text_message'] = $messageTemplate->msg_text
;
106 $htmlMessage = $messageTemplate->msg_html
;
109 if (isset($defaults['body_text'])) {
110 $defaults['text_message'] = $defaults['body_text'];
111 $this->set('textFile', $defaults['body_text']);
112 $this->set('skipTextFile', TRUE);
115 if (isset($defaults['body_html'])) {
116 $htmlMessage = $defaults['body_html'];
117 $this->set('htmlFile', $defaults['body_html']);
118 $this->set('skipHtmlFile', TRUE);
121 //set default from email address.
122 if (!empty($defaults['from_name']) && !empty($defaults['from_email'])) {
123 $defaults['from_email_address'] = array_search('"' . $defaults['from_name'] . '" <' . $defaults['from_email'] . '>',
124 CRM_Core_OptionGroup
::values('from_email_address')
128 //get the default from email address.
129 $defaultAddress = CRM_Core_OptionGroup
::values('from_email_address', NULL, NULL, NULL, ' AND is_default = 1');
130 foreach ($defaultAddress as $id => $value) {
131 $defaults['from_email_address'] = $id;
135 if (!empty($defaults['replyto_email'])) {
136 $replyToEmail = CRM_Core_OptionGroup
::values('from_email_address');
137 foreach ($replyToEmail as $value) {
138 if (strstr($value, $defaults['replyto_email'])) {
139 $replyToEmailAddress = $value;
143 $replyToEmailAddress = explode('<', $replyToEmailAddress);
144 if (count($replyToEmailAddress) > 1) {
145 $replyToEmailAddress = $replyToEmailAddress[0] . '<' . $replyToEmailAddress[1];
147 $defaults['reply_to_address'] = array_search($replyToEmailAddress, $replyToEmail);
152 if (!$reuseMailing) {
153 $textFilePath = $this->get('textFilePath');
155 file_exists($textFilePath)
157 $defaults['text_message'] = file_get_contents($textFilePath);
158 if (strlen($defaults['text_message']) > 0) {
159 $this->set('skipTextFile', TRUE);
163 $htmlFilePath = $this->get('htmlFilePath');
165 file_exists($htmlFilePath)
167 $defaults['html_message'] = file_get_contents($htmlFilePath);
168 if (strlen($defaults['html_message']) > 0) {
169 $htmlMessage = $defaults['html_message'];
170 $this->set('skipHtmlFile', TRUE);
175 if ($this->get('html_message')) {
176 $htmlMessage = $this->get('html_message');
179 $htmlMessage = str_replace(array("\n", "\r"), ' ', $htmlMessage);
180 $htmlMessage = str_replace("'", "\'", $htmlMessage);
181 $this->assign('message_html', $htmlMessage);
183 $defaults['upload_type'] = 1;
184 if (isset($defaults['body_html'])) {
185 $defaults['html_message'] = $defaults['body_html'];
188 //CRM-4678 setdefault to default component when composing new mailing.
189 if (!$reuseMailing) {
190 $componentFields = array(
191 'header_id' => 'Header',
192 'footer_id' => 'Footer',
194 foreach ($componentFields as $componentVar => $componentType) {
195 $defaults[$componentVar] = CRM_Mailing_PseudoConstant
::defaultComponent($componentType, '');
203 * Function to actually build the form
208 public function buildQuickForm() {
209 $session = CRM_Core_Session
::singleton();
210 $config = CRM_Core_Config
::singleton();
214 // this seems so hacky, not sure what we are doing here and why. Need to investigate and fix
215 $session->getVars($options,
216 "CRM_Mailing_Controller_Send_{$this->controller->_key}"
219 $fromEmailAddress = CRM_Core_OptionGroup
::values('from_email_address');
220 if (empty($fromEmailAddress)) {
221 //redirect user to enter from email address.
222 $url = CRM_Utils_System
::url('civicrm/admin/options/from_email_address', 'action=add&reset=1');
223 $status = ts("There is no valid from email address present. You can add here <a href='%1'>Add From Email Address.</a>", array(1 => $url));
224 $session->setStatus($status, ts('Notice'));
227 foreach ($fromEmailAddress as $key => $email) {
228 $fromEmailAddress[$key] = htmlspecialchars($fromEmailAddress[$key]);
232 $this->add('select', 'from_email_address',
233 ts('From Email Address'), array(
234 '' => '- select -') +
$fromEmailAddress, TRUE
237 //Added code to add custom field as Reply-To on form when it is enabled from Mailer settings
238 if (isset($config->replyTo
) && !empty($config->replyTo
) && empty($options['override_verp'])) {
239 $this->add('select', 'reply_to_address', ts('Reply-To'),
240 array('' => '- select -') +
$fromEmailAddress
243 elseif (!empty($options['override_verp'])) {
244 $trackReplies = TRUE;
245 $this->assign('trackReplies', $trackReplies);
248 $this->add('text', 'subject', ts('Mailing Subject'),
249 CRM_Core_DAO
::getAttribute('CRM_Mailing_DAO_Mailing', 'subject'), TRUE
252 $attributes = array('onclick' => "showHideUpload();");
253 $options = array(ts('Upload Content'), ts('Compose On-screen'));
255 $this->addRadio('upload_type', ts('I want to'), $options, $attributes, " ");
257 CRM_Mailing_BAO_Mailing
::commonCompose($this);
259 $this->addElement('file', 'textFile', ts('Upload TEXT Message'), 'size=30 maxlength=60');
260 $this->addUploadElement('textFile');
261 $this->setMaxFileSize(1024 * 1024);
262 $this->addRule('textFile', ts('File size should be less than 1 MByte'), 'maxfilesize', 1024 * 1024);
263 $this->addRule('textFile', ts('File must be in UTF-8 encoding'), 'utf8File');
265 $this->addElement('file', 'htmlFile', ts('Upload HTML Message'), 'size=30 maxlength=60');
266 $this->addUploadElement('htmlFile');
267 $this->setMaxFileSize(1024 * 1024);
268 $this->addRule('htmlFile',
269 ts('File size should be less than %1 MByte(s)',
275 $this->addRule('htmlFile', ts('File must be in UTF-8 encoding'), 'utf8File');
277 //fix upload files when context is search. CRM-3711
278 $ssID = $this->get('ssID');
279 if ($this->_searchBasedMailing
&& $ssID) {
280 $this->set('uploadNames', array('textFile', 'htmlFile'));
283 CRM_Core_BAO_File
::buildAttachment($this,
288 $this->add('select', 'header_id', ts('Mailing Header'),
289 array('' => ts('- none -')) + CRM_Mailing_PseudoConstant
::component('Header')
292 $this->add('select', 'footer_id', ts('Mailing Footer'),
293 array('' => ts('- none -')) + CRM_Mailing_PseudoConstant
::component('Footer')
296 $this->addFormRule(array('CRM_Mailing_Form_Upload', 'formRule'), $this);
299 array('type' => 'back',
300 'name' => ts('<< Previous'),
304 'name' => ts('Next >>'),
305 'spacing' => ' ',
310 'name' => ts('Save & Continue Later'),
315 'name' => ts('Cancel'),
318 $this->addButtons($buttons);
321 public function postProcess() {
322 $params = $ids = array();
323 $uploadParams = array('header_id', 'footer_id', 'subject', 'from_name', 'from_email');
324 $fileType = array('textFile', 'htmlFile');
326 $formValues = $this->controller
->exportValues($this->_name
);
328 foreach ($uploadParams as $key) {
329 if (!empty($formValues[$key]) ||
330 in_array($key, array('header_id', 'footer_id'))
332 $params[$key] = $formValues[$key];
333 $this->set($key, $formValues[$key]);
337 if (!$formValues['upload_type']) {
338 foreach ($fileType as $key) {
340 if (isset($formValues[$key]) &&
341 !empty($formValues[$key])
343 $contents = file_get_contents($formValues[$key]['name']);
344 $this->set($key, $formValues[$key]['name']);
347 $params['body_' . substr($key, 0, 4)] = $contents;
350 $params['body_' . substr($key, 0, 4)] = 'NULL';
355 $text_message = $formValues['text_message'];
356 $params['body_text'] = $text_message;
357 $this->set('textFile', $params['body_text']);
358 $this->set('text_message', $params['body_text']);
359 $html_message = $formValues['html_message'];
361 // dojo editor does some html conversion when tokens are
362 // inserted as links. Hence token replacement fails.
363 // this is hack to revert html conversion for { to %7B and
364 // } to %7D by dojo editor
365 $html_message = str_replace('%7B', '{', str_replace('%7D', '}', $html_message));
367 $params['body_html'] = $html_message;
368 $this->set('htmlFile', $params['body_html']);
369 $this->set('html_message', $params['body_html']);
372 $params['name'] = $this->get('name');
374 $session = CRM_Core_Session
::singleton();
375 $params['contact_id'] = $session->get('userID');
376 $composeFields = array(
377 'template', 'saveTemplate',
378 'updateTemplate', 'saveTemplateName',
381 //mail template is composed
382 if ($formValues['upload_type']) {
383 $composeParams = array();
385 foreach ($composeFields as $key) {
386 if (!empty($formValues[$key])) {
387 $composeParams[$key] = $formValues[$key];
388 $this->set($key, $formValues[$key]);
392 if (!empty($composeParams['updateTemplate'])) {
393 $templateParams = array(
394 'msg_text' => $text_message,
395 'msg_html' => $html_message,
396 'msg_subject' => $params['subject'],
400 $templateParams['id'] = $formValues['template'];
402 $msgTemplate = CRM_Core_BAO_MessageTemplate
::add($templateParams);
405 if (!empty($composeParams['saveTemplate'])) {
406 $templateParams = array(
407 'msg_text' => $text_message,
408 'msg_html' => $html_message,
409 'msg_subject' => $params['subject'],
413 $templateParams['msg_title'] = $composeParams['saveTemplateName'];
415 $msgTemplate = CRM_Core_BAO_MessageTemplate
::add($templateParams);
418 if (isset($msgTemplate->id
)) {
419 $params['msg_template_id'] = $msgTemplate->id
;
422 $params['msg_template_id'] = CRM_Utils_Array
::value('template', $formValues);
424 $this->set('template', $params['msg_template_id']);
427 CRM_Core_BAO_File
::formatAttachment($formValues,
432 $ids['mailing_id'] = $this->_mailingID
;
434 //handle mailing from name & address.
435 $fromEmailAddress = CRM_Utils_Array
::value($formValues['from_email_address'],
436 CRM_Core_OptionGroup
::values('from_email_address')
439 //get the from email address
440 $params['from_email'] = CRM_Utils_Mail
::pluckEmailFromHeader($fromEmailAddress);
443 $params['from_name'] = CRM_Utils_Array
::value(1, explode('"', $fromEmailAddress));
445 //Add Reply-To to headers
446 if (!empty($formValues['reply_to_address'])) {
447 $replyToEmail = CRM_Core_OptionGroup
::values('from_email_address');
448 $params['replyto_email'] = CRM_Utils_Array
::value($formValues['reply_to_address'], $replyToEmail);
451 /* Build the mailing object */
453 CRM_Mailing_BAO_Mailing
::create($params, $ids);
455 if (isset($this->_submitValues
['_qf_Upload_upload_save']) &&
456 $this->_submitValues
['_qf_Upload_upload_save'] == 'Save & Continue Later'
458 //when user perform mailing from search context
459 //redirect it to search result CRM-3711.
460 $ssID = $this->get('ssID');
461 if ($ssID && $this->_searchBasedMailing
) {
462 if ($this->_action
== CRM_Core_Action
::BASIC
) {
463 $fragment = 'search';
465 elseif ($this->_action
== CRM_Core_Action
::PROFILE
) {
466 $fragment = 'search/builder';
468 elseif ($this->_action
== CRM_Core_Action
::ADVANCED
) {
469 $fragment = 'search/advanced';
472 $fragment = 'search/custom';
475 $context = $this->get('context');
476 if (!CRM_Contact_Form_Search
::isSearchContext($context)) {
479 $urlParams = "force=1&reset=1&ssID={$ssID}&context={$context}";
480 $qfKey = CRM_Utils_Request
::retrieve('qfKey', 'String', $this);
481 if (CRM_Utils_Rule
::qfKey($qfKey)) {
482 $urlParams .= "&qfKey=$qfKey";
485 $session = CRM_Core_Session
::singleton();
486 $draftURL = CRM_Utils_System
::url('civicrm/mailing/browse/unscheduled', 'scheduled=false&reset=1');
487 $status = ts("You can continue later by clicking the 'Continue' action to resume working on it.<br />From <a href='%1'>Draft and Unscheduled Mailings</a>.", array(1 => $draftURL));
488 CRM_Core_Session
::setStatus($status, ts('Mailing Saved'), 'success');
490 // Redirect user to search.
491 $url = CRM_Utils_System
::url('civicrm/contact/' . $fragment, $urlParams);
494 $status = ts("Click the 'Continue' action to resume working on it.");
495 $url = CRM_Utils_System
::url('civicrm/mailing/browse/unscheduled', 'scheduled=false&reset=1');
497 CRM_Core_Session
::setStatus($status, ts('Mailing Saved'), 'success');
498 return $this->controller
->setDestination($url);
503 * Function for validation
505 * @param array $params (ref.) an assoc array of name/value pairs
507 * @return mixed true or array of errors
511 static function formRule($params, $files, $self) {
512 if (!empty($_POST['_qf_Import_refresh'])) {
516 $template = CRM_Core_Smarty
::singleton();
519 if (isset($params['html_message'])) {
520 $htmlMessage = str_replace(array("\n", "\r"), ' ', $params['html_message']);
521 $htmlMessage = str_replace("'", "\'", $htmlMessage);
522 $template->assign('htmlContent', $htmlMessage);
525 $domain = CRM_Core_BAO_Domain
::getDomain();
527 $mailing = new CRM_Mailing_BAO_Mailing();
528 $mailing->id
= $self->_mailingID
;
529 $mailing->find(TRUE);
531 $session = CRM_Core_Session
::singleton();
532 $values = array('contact_id' => $session->get('userID'),
535 require_once 'api/api.php';
536 $contact = civicrm_api('contact', 'get', $values);
539 $contact = reset($contact['values']);
541 $verp = array_flip(array('optOut', 'reply', 'unsubscribe', 'resubscribe', 'owner'));
542 foreach ($verp as $key => $value) {
546 $urls = array_flip(array('forward', 'optOutUrl', 'unsubscribeUrl', 'resubscribeUrl'));
547 foreach ($urls as $key => $value) {
552 // set $header and $footer
554 'header', 'footer') as $part) {
556 if ($params["{$part}_id"]) {
558 $component = new CRM_Mailing_BAO_Component();
559 $component->id
= $params["{$part}_id"];
560 $component->find(TRUE);
561 $
{$part}['textFile'] = $component->body_text
;
562 $
{$part}['htmlFile'] = $component->body_html
;
566 $
{$part}['htmlFile'] = $
{$part}['textFile'] = '';
571 $skipTextFile = $self->get('skipTextFile');
572 $skipHtmlFile = $self->get('skipHtmlFile');
574 if (!$params['upload_type']) {
575 if ((!isset($files['textFile']) ||
!file_exists($files['textFile']['tmp_name'])) &&
576 (!isset($files['htmlFile']) ||
!file_exists($files['htmlFile']['tmp_name']))
578 if (!($skipTextFile ||
$skipHtmlFile)) {
579 $errors['textFile'] = ts('Please provide either a Text or HTML formatted message - or both.');
584 if (empty($params['text_message']) && empty($params['html_message'])) {
585 $errors['html_message'] = ts('Please provide either a Text or HTML formatted message - or both.');
587 if (!empty($params['saveTemplate']) && empty($params['saveTemplateName'])) {
588 $errors['saveTemplateName'] = ts('Please provide a Template Name.');
593 'text', 'html') as $file) {
594 if (!$params['upload_type'] && !file_exists(CRM_Utils_Array
::value('tmp_name', $files[$file . 'File']))) {
597 if ($params['upload_type'] && !$params[$file . '_message']) {
601 if (!$params['upload_type']) {
602 $str = file_get_contents($files[$file . 'File']['tmp_name']);
603 $name = $files[$file . 'File']['name'];
606 $str = $params[$file . '_message'];
607 $str = ($file == 'html') ?
str_replace('%7B', '{', str_replace('%7D', '}', $str)) : $str;
608 $name = $file . ' message';
611 /* append header/footer */
613 $str = $header[$file . 'File'] . $str . $footer[$file . 'File'];
615 $dataErrors = array();
617 /* First look for missing tokens */
619 if (!CRM_Core_BAO_Setting
::getItem(CRM_Core_BAO_Setting
::MAILING_PREFERENCES_NAME
, 'disable_mandatory_tokens_check')) {
620 $err = CRM_Utils_Token
::requiredTokens($str);
622 foreach ($err as $token => $desc) {
623 $dataErrors[] = '<li>' . ts('This message is missing a required token - {%1}: %2',
624 array(1 => $token, 2 => $desc)
630 /* Do a full token replacement on a dummy verp, the current
631 * contact and domain, and the first organization. */
634 // here we make a dummy mailing object so that we
635 // can retrieve the tokens that we need to replace
636 // so that we do get an invalid token error
637 // this is qute hacky and I hope that there might
638 // be a suggestion from someone on how to
639 // make it a bit more elegant
641 $dummy_mail = new CRM_Mailing_BAO_Mailing();
642 $mess = "body_{$file}";
643 $dummy_mail->$mess = $str;
644 $tokens = $dummy_mail->getTokens();
646 $str = CRM_Utils_Token
::replaceSubscribeInviteTokens($str);
647 $str = CRM_Utils_Token
::replaceDomainTokens($str, $domain, NULL, $tokens[$file]);
648 $str = CRM_Utils_Token
::replaceMailingTokens($str, $mailing, NULL, $tokens[$file]);
649 $str = CRM_Utils_Token
::replaceOrgTokens($str, $org);
650 $str = CRM_Utils_Token
::replaceActionTokens($str, $verp, $urls, NULL, $tokens[$file]);
651 $str = CRM_Utils_Token
::replaceContactTokens($str, $contact, NULL, $tokens[$file]);
653 $unmatched = CRM_Utils_Token
::unmatchedTokens($str);
655 if (!empty($unmatched) && 0) {
656 foreach ($unmatched as $token) {
657 $dataErrors[] = '<li>' . ts('Invalid token code') . ' {' . $token . '}</li>';
660 if (!empty($dataErrors)) {
661 $errors[$file . 'File'] = ts('The following errors were detected in %1:', array(
662 1 => $name)) . ' <ul>' . implode('', $dataErrors) . '</ul><br /><a href="' . CRM_Utils_System
::docURL2('Sample CiviMail Messages', TRUE, NULL, NULL, NULL, "wiki") . '" target="_blank">' . ts('More information on required tokens...') . '</a>';
666 $templateName = CRM_Core_BAO_MessageTemplate
::getMessageTemplates();
667 if (!empty($params['saveTemplate']) && in_array(CRM_Utils_Array
::value('saveTemplateName', $params), $templateName)
669 $errors['saveTemplate'] = ts('Duplicate Template Name.');
671 return empty($errors) ?
TRUE : $errors;
675 * Display Name of the form
681 public function getTitle() {
682 return ts('Mailing Content');