useTransaction(); parent::setUp(); CRM_Mailing_BAO_MailingJob::$mailsProcessed = 0; // DGW $this->_groupID = $this->groupCreate(); $this->createContactsInGroup(2, $this->_groupID); $this->defaultParams = array( 'name' => 'mailing name', 'created_id' => 1, 'groups' => array('include' => array($this->_groupID)), 'scheduled_date' => 'now', ); $this->_mut = new CiviMailUtils($this, TRUE); $this->callAPISuccess('mail_settings', 'get', array('api.mail_settings.create' => array('domain' => 'chaos.org'))); } /** */ public function tearDown() { $this->_mut->stop(); CRM_Utils_Hook::singleton()->reset(); CRM_Mailing_BAO_MailingJob::$mailsProcessed = 0; // DGW parent::tearDown(); } /** * Generate a fully-formatted mailing with standard email headers. */ public function testBasicHeaders() { $allMessages = $this->runMailingSuccess(array( 'subject' => 'Accidents in cars cause children for {contact.display_name}!', 'body_text' => 'BEWARE children need regular infusions of toys. Santa knows your {domain.address}. There is no {action.optOutUrl}.', )); foreach ($allMessages as $k => $message) { /** @var ezcMail $message */ $offset = $k + 1; $this->assertEquals("FIXME", $message->from->name); $this->assertEquals("info@EXAMPLE.ORG", $message->from->email); $this->assertEquals("Mr. Foo{$offset} Anderson II", $message->to[0]->name); $this->assertEquals("mail{$offset}@nul.example.com", $message->to[0]->email); $this->assertRegExp('#^text/plain; charset=utf-8#', $message->headers['Content-Type']); $this->assertRegExp(';^b\.[\d\.a-f]+@chaos.org$;', $message->headers['Return-Path']); $this->assertRegExp(';^b\.[\d\.a-f]+@chaos.org$;', $message->headers['X-CiviMail-Bounce'][0]); $this->assertRegExp(';^\$;', $message->headers['List-Unsubscribe'][0]); $this->assertEquals('bulk', $message->headers['Precedence'][0]); } } /** * Generate a fully-formatted mailing (with body_text content). */ public function testText() { $allMessages = $this->runMailingSuccess(array( 'subject' => 'Accidents in cars cause children for {contact.display_name}!', 'body_text' => 'BEWARE children need regular infusions of toys. Santa knows your {domain.address}. There is no {action.optOutUrl}.', 'open_tracking' => 1, // Note: open_tracking does nothing with text, but we'll just verify that it does nothing )); foreach ($allMessages as $k => $message) { /** @var ezcMail $message */ /** @var ezcMailText $textPart */ $this->assertTrue($message->body instanceof ezcMailText); $this->assertEquals('plain', $message->body->subType); $this->assertRegExp( ";" . "Sample Header for TEXT formatted content.\n" . // Default header "BEWARE children need regular infusions of toys. Santa knows your .*\\. There is no http.*civicrm/mailing/optout.*\\.\n" . "to unsubscribe: http.*civicrm/mailing/optout" . // Default footer ";", $message->body->text ); } } /** * Generate a fully-formatted mailing (with body_html content). */ public function testHtmlWithOpenTracking() { $allMessages = $this->runMailingSuccess(array( 'subject' => 'Example Subject', 'body_html' => '

You can go to Google or opt out.

', 'open_tracking' => 1, 'url_tracking' => 0, )); foreach ($allMessages as $k => $message) { /** @var ezcMail $message */ /** @var ezcMailText $htmlPart */ /** @var ezcMailText $textPart */ $this->assertTrue($message->body instanceof ezcMailMultipartAlternative); list($textPart, $htmlPart) = $message->body->getParts(); $this->assertEquals('html', $htmlPart->subType); $this->assertRegExp( ";" . "Sample Header for HTML formatted content.\n" . // Default header // FIXME: CiviMail puts double " after hyperlink! "

You can go to Google or opt out.

\n" . // body_html "Sample Footer for HTML formatted content" . // Default footer ".*\n" . "text ); $this->assertEquals('plain', $textPart->subType); $this->assertRegExp( ";" . "Sample Header for TEXT formatted content.\n" . // Default header "You can go to Google \\[1\\] or opt out \\[2\\]\\.\n" . // body_html, filtered "\n" . "Links:\n" . "------\n" . "\\[1\\] http://example.net/first\\?cs=[0-9a-f_]+\n" . "\\[2\\] http.*civicrm/mailing/optout.*\n" . "\n" . "to unsubscribe: http.*civicrm/mailing/optout" . // Default footer ";", $textPart->text ); } } /** * Generate a fully-formatted mailing (with body_html content). */ public function testHtmlWithOpenAndUrlTracking() { $allMessages = $this->runMailingSuccess(array( 'subject' => 'Example Subject', 'body_html' => '

You can go to Google or opt out.

', 'open_tracking' => 1, 'url_tracking' => 1, )); foreach ($allMessages as $k => $message) { /** @var ezcMail $message */ /** @var ezcMailText $htmlPart */ /** @var ezcMailText $textPart */ $this->assertTrue($message->body instanceof ezcMailMultipartAlternative); list($textPart, $htmlPart) = $message->body->getParts(); $this->assertEquals('html', $htmlPart->subType); $this->assertRegExp( ";" . // body_html "

You can go to Google" . " or opt out.

\n" . // Default footer "Sample Footer for HTML formatted content" . ".*\n" . // Open-tracking code "text ); $this->assertEquals('plain', $textPart->subType); $this->assertRegExp( ";" . // body_html, filtered "You can go to Google \\[1\\] or opt out \\[2\\]\\.\n" . "\n" . "Links:\n" . "------\n" . "\\[1\\] .*extern/url\.php\?u=\d+&qid=\d+\n" . "\\[2\\] http.*civicrm/mailing/optout.*\n" . "\n" . // Default footer "to unsubscribe: http.*civicrm/mailing/optout" . ";", $textPart->text ); } } public function urlTrackingExamples() { $cases = array(); // Each case comes in four parts: // 1. Mailing HTML (body_html) // 2. Regex to run against final HTML // 3. Regex to run against final text // 4. Additional mailing options // Tracking disabled $cases[] = array( '

Foo

', ';

Foo

;', ';\\[1\\] http://example\.net/;', array('url_tracking' => 0), ); $cases[] = array( '

Foo

', // FIXME: Legacy tracker adds extra quote after URL ';

Foo

;', ';\\[1\\] http://example\.net/\?id=\d+;', array('url_tracking' => 0), ); $cases[] = array( '

Foo

', ';

Foo

;', ';\\[1\\] http.*civicrm/mailing/optout.*;', array('url_tracking' => 0), ); $cases[] = array( '

Look at .

', ';

Look at \.

;', ';Look at \.;', array('url_tracking' => 0), ); $cases[] = array( // Plain-text URL's are tracked in plain-text emails... // but not in HTML emails. "

Please go to: http://example.net/

", ";

Please go to: http://example\.net/

;", ';Please go to: http://example\.net/;', array('url_tracking' => 0), ); // Tracking enabled $cases[] = array( '

Foo

', ';

Foo

;', ';\\[1\\] .*extern/url\.php\?u=\d+.*;', array('url_tracking' => 1), ); $cases[] = array( // FIXME: CiviMail URL tracking doesn't track tokenized links. '

Foo

', // FIXME: Legacy tracker adds extra quote after URL ';

Foo

;', ';\\[1\\] http://example\.net/\?id=\d+;', array('url_tracking' => 1), ); $cases[] = array( // It would be redundant/slow to track the action URLs? '

Foo

', ';

Foo

;', ';\\[1\\] http.*civicrm/mailing/optout.*;', array('url_tracking' => 1), ); $cases[] = array( // It would be excessive/slow to track every embedded image. '

Look at .

', ';

Look at \.

;', ';Look at \.;', array('url_tracking' => 1), ); $cases[] = array( // Plain-text URL's are tracked in plain-text emails... // but not in HTML emails. "

Please go to: http://example.net/

", ";

Please go to: http://example\.net/

;", ';Please go to: .*extern/url.php\?u=\d+&qid=\d+;', array('url_tracking' => 1), ); return $cases; } /** * Generate a fully-formatted mailing (with body_html content). * * @dataProvider urlTrackingExamples */ public function testUrlTracking($inputHtml, $htmlUrlRegex, $textUrlRegex, $params) { $caseName = print_r(array('inputHtml' => $inputHtml, 'params' => $params), 1); $allMessages = $this->runMailingSuccess($params + array( 'subject' => 'Example Subject', 'body_html' => $inputHtml, )); foreach ($allMessages as $k => $message) { /** @var ezcMail $message */ /** @var ezcMailText $htmlPart */ /** @var ezcMailText $textPart */ $this->assertTrue($message->body instanceof ezcMailMultipartAlternative); list($textPart, $htmlPart) = $message->body->getParts(); if ($htmlUrlRegex) { $this->assertEquals('html', $htmlPart->subType, "Should have HTML part in case: $caseName"); $this->assertRegExp($htmlUrlRegex, $htmlPart->text, "Should have correct HTML in case: $caseName"); } if ($textUrlRegex) { $this->assertEquals('plain', $textPart->subType, "Should have text part in case: $caseName"); $this->assertRegExp($textUrlRegex, $textPart->text, "Should have correct text in case: $caseName"); } } } /** * Create contacts in group. * * @param int $count * @param int $groupID * @param string $domain */ protected function createContactsInGroup( $count, $groupID, $domain = 'nul.example.com' ) { for ($i = 1; $i <= $count; $i++) { $contactID = $this->individualCreate(array( 'first_name' => "Foo{$i}", 'email' => 'mail' . $i . '@' . $domain, )); $this->callAPISuccess('group_contact', 'create', array( 'contact_id' => $contactID, 'group_id' => $groupID, 'status' => 'Added', )); } } /** * Create and execute a mailing. Return the matching messages. * * @param array $params * List of parameters to send to Mailing.create API. * @return array */ protected function runMailingSuccess($params) { $mailingParams = array_merge($this->defaultParams, $params); $this->callAPISuccess('mailing', 'create', $mailingParams); $this->_mut->assertRecipients(array()); $this->callAPISuccess('job', 'process_mailing', array('runInNonProductionEnvironment' => TRUE)); $allMessages = $this->_mut->getAllMessages('ezc'); // There are exactly two contacts produced by setUp(). $this->assertEquals(2, count($allMessages)); return $allMessages; } }