private function addEvent($type, $id, $email, $datetime) { echo 'Adding ' . strtoupper($type) . ' to campaign ' . $id . ' with date of ' . $datetime . ' and email of ' . $email . "\r\n"; $StoreModel = new Store(); $encryptedEmail = $StoreModel->encryptEmail($email); // Look for this email address in store table $StoreRows = Store::model()->with('store2contact')->findAll(array('condition' => 'email = :email', 'params' => array(':email' => $encryptedEmail))); // collect our warehouse_ids up to match in campaign_contact table. $warehouseIDs = []; if (sizeof($StoreRows)) { // Save 1 suppression row for every instance of the email address in the store table - use store_id foreach ($StoreRows as $Store) { if ($Store->store2contact != null) { $warehouseIDs[] = $Store->store2contact->contact_warehouse_id; } } $Contacts = null; // check for contact if (sizeof($warehouseIDs) && is_numeric($id)) { //Bounces if ($type === 'bounce') { $Contacts = CampaignContact::model()->updateAll(array('bounced' => $datetime), "campaign_id = :campaign_id AND warehouse_id IN (" . implode(',', $warehouseIDs) . ") AND bounced IS NULL", array(':campaign_id' => $id)); } else { $Contacts = CampaignContact::model()->updateAll(array('opened' => $datetime), "campaign_id = :campaign_id AND warehouse_id IN (" . implode(',', $warehouseIDs) . ") AND opened IS NULL", array(':campaign_id' => $id)); } } echo 'Updated ' . sizeof($Contacts) . ' contact'; } else { echo 'Campaign contact not found' . "\r\n"; } echo "\r\n"; }
public function actionExport($id) { $Campaign = Campaign::model()->findByPk($_GET['campaign_id']); if (!$Campaign->hasBeenRun) { throw new CHttpException('404', 'Not found'); } // check campaign has been run if (!$Campaign->hasBeenRun) { throw new CHttpException('403', 'Forbidden'); // not possible. Forbidden. } $CampaignGroup = CampaignGroup::model()->findByAttributes(array('id' => $id, 'campaign_id' => $Campaign->id)); if (is_null($CampaignGroup)) { throw new CHttpException('404', 'Not found'); } $Criteria = new CDbCriteria(); $Criteria->compare('`t`.`campaign_id`', $Campaign->id); $Criteria->compare('`t`.`group_id`', $id); $Criteria->with = array('contact'); $Criteria->order = '`t`.`group_id` ASC'; $CampaignContacts = CampaignContact::model()->findAll($Criteria); // build the csv $rows = array(); $outcomeColumns = array(); foreach ($CampaignGroup->outcomes as $Outcome) { // will be a url or NULL so fine $outcomeColumns[$Outcome->name] = $Outcome->url; } $headings = array('contact_warehouse_id' => 'insider_id', 'salutation' => 'Prefix', 'first_name' => 'Forename', 'last_name' => 'Surname', 'email' => 'Email', 'phone' => 'Telephone', 'mobile' => 'Mobile', 'dob' => 'Date of Birth', 'address_line_1' => 'Address Line 1', 'address_line_2' => 'Address Line 2', 'address_line_3' => 'Address Line 3', 'address_line_4' => 'Address Line 4', 'address_town' => 'Town', 'address_postcode' => 'Postcode', 'address_county' => 'County', 'culture_segment' => 'Culture Segment', 'level_of_engagement' => 'Level of Engagement'); // build each row foreach ($CampaignContacts as $CampaignContact) { $row = array(); // use headings to get data foreach ($headings as $key => $value) { $row[$value] = $CampaignContact->contact->{$key}; } $rows[] = array_merge($row, array_values($outcomeColumns)); } // stick headers on the start after adding outcome names to it array_unshift($rows, array_values(array_merge($headings, array_keys($outcomeColumns)))); $cleanGroupName = preg_replace(array("@[^a-z0-9\\-\\s]@i", "@\\s+@", "@\\-{2,}@"), array("", "-", "-"), $CampaignGroup->name) . '-' . date("Y-m-d"); $csv = fopen('php://output', 'w'); header("Expires: Tue, 03 Jul 2001 06:00:00 GMT"); header("Cache-Control: max-age=0, no-cache, must-revalidate, proxy-revalidate"); header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); header('Content-Encoding: UTF-8'); header('Content-type: text/csv; charset=UTF-8'); // disposition / encoding on response body header("Content-Disposition: attachment;filename=" . $cleanGroupName . ".csv"); header("Content-Transfer-Encoding: binary"); foreach ($rows as $column) { fputcsv($csv, $column); } fclose($csv); //echo $csv; exit; }
public function run($args) { $campaignSuccess = false; $configs = Yii::app()->db->createCommand("SELECT * FROM `config` WHERE `key` IN ('host', 'https')")->queryAll(); if (sizeof($configs) < 2) { throw new CException("\n\n\n===\nDomain or HTTPS config not yet set. View any admin area page in a browser to remedy this.\n===\n\n"); } foreach ($configs as $config) { $configParams[$config['key']] = $config['value']; } date_default_timezone_set("Europe/London"); print "Getting campaigns to process \n"; $CampaignCollection = Campaign::model()->findAll(array('with' => array('query', 'groups' => array('with' => array('email_template'))), "condition" => "processing = 0 AND status = :status AND invite = 0", "params" => array(":status" => Campaign::STATUS_QUEUED))); print count($CampaignCollection) . ' campaigns to process' . "\n"; $campaignIDs = []; foreach ($CampaignCollection as $Campaign) { $campaignIDs[] = $Campaign->id; print "Will process Campaign ID: " . $Campaign->id . " \n"; } $command = Yii::app()->db->createCommand(); $command->update('campaign', array("processing" => 1), array('in', 'id', $campaignIDs)); foreach ($CampaignCollection as $Campaign) { /* sending logic */ /* create mailgun campaign id */ $mailgunApi = new MailgunCampaign(Yii::app()->params['insiderEmailDomain'], Yii::app()->params['mailgun']['key']); // Check if the campaign ID exists try { $checkCampaignResponse = $mailgunApi->getCampaign(Yii::app()->params['insiderEmailDomain'], $Campaign->id); } catch (Exception $e) { $checkCampaignResponse = false; } if (!$checkCampaignResponse) { $mailgunCampaignResponse = $mailgunApi->createCampaign(Yii::app()->params['insiderEmailDomain'], array("name" => $Campaign->name, "id" => $Campaign->id)); } else { $mailgunCampaignResponse['campaign'] = $checkCampaignResponse; } /* Example Response Array ( [message] => Campaign created [campaign] => Array ( [clicked_count] => 0 [opened_count] => 0 [submitted_count] => 0 [unsubscribed_count] => 0 [bounced_count] => 0 [id] => 691 [name] => Hicks 2 [created_at] => Tue, 24 Feb 2015 13:34:25 GMT [delivered_count] => 0 [complained_count] => 0 [dropped_count] => 0 ) ) */ $Store = new Store(); /* loop groups */ /* load & render template (campaign_group) */ foreach ($Campaign->groups as $CampaignGroup) { $mailgunApi = new MailgunApi(Yii::app()->params['insiderEmailDomain'], Yii::app()->params['mailgun']['key']); $mailgunApi->enableTracking(); $mailgunApi->enableOpensTracking(); $mailgunApi->enableClicksTracking(); $message = $mailgunApi->newMessage(); $message->setFrom(Yii::app()->params['fromEmail'], Yii::app()->name); $message->setSubject($CampaignGroup->subject); $message->setCampaignId($mailgunCampaignResponse['campaign']['id']); $message->setHtml($CampaignGroup->email_template->parsedHtml($configParams)); // send variables to mailgun so that bounces and opens are easier to deal with. $message->addVar('campaign_id', $Campaign->id); $message->addVar('group_id', $CampaignGroup->id); //get contacts $CampaignContactCollection = CampaignContact::model()->findAll(array('with' => array('contact2outcomes' => array('index' => 'campaign_outcome_id')), "condition" => "\n\t\t\t\t\t\tgroup_id = :groupid\n\t\t\t\t\t\tAND `t`.`status` = 0\n\t\t\t\t\t", "params" => array(":groupid" => $CampaignGroup->id), 'index' => 'warehouse_id')); echo sizeof($CampaignContactCollection); // get the contact rows for the above campaigncontact rows $Criteria = new CDbCriteria(); $Criteria->index = 'contact_warehouse_id'; $Contacts = CleanWarehouse::model()->findAllByPk(array_keys($CampaignContactCollection), $Criteria); $chunkedCampaignContacts = array_chunk($CampaignContactCollection, 1000, true); foreach ($chunkedCampaignContacts as $campaign1000) { //echo 'C'; $transaction = $Campaign->dbConnection->beginTransaction(); $campaign1000IDs = array(); $sentCount = 0; $message->resetTo(); foreach ($campaign1000 as $warehouse_id => $CampaignContact) { //echo '.'; $campaign1000IDs[] = $CampaignContact->id; $thisContact = $Contacts[$warehouse_id]; $standardTags = array('first_name' => $thisContact->first_name, 'last_name' => $Store->decryptLastName($thisContact->last_name), 'email' => $Store->decryptEmail($thisContact->email), 'insider_unsubscribe' => 'http' . ($configParams['https'] ? 's' : '') . '://' . $configParams['host'] . Yii::app()->urlManager->createUrl('data/campaignUnsubscribe', array('campaign_id' => $Campaign->id, 'campaign_hash' => $Campaign->hash, 'campaign_contact_id' => $CampaignContact->id, 'campaign_contact_hash' => $CampaignContact->hash)), 'warehouse_id' => $warehouse_id); $campaignTags = $CampaignGroup->email_template->returnOutcomeTagsToUrls($configParams, $Campaign, $CampaignContact->contact2outcomes); $parsedTagsArray = array_merge($standardTags, $campaignTags); if (ENVIRONMENT === 'PRODUCTION') { $message->addTo($Store->decryptEmail($thisContact->email), $thisContact->first_name . ' ' . $Store->decryptLastName($thisContact->last_name), $parsedTagsArray); } else { $message->addTo("*****@*****.**", $thisContact->first_name . ' ' . $Store->decryptLastName($thisContact->last_name), $parsedTagsArray); } $sentCount++; } /* send email to mailgun */ try { $response = $message->send(); $command = Yii::app()->db->createCommand(); $command->update('campaign_contact', array("status" => CampaignContact::STATUS_SENT, "processing" => 0), array('in', 'id', $campaign1000IDs)); $transaction->commit(); $campaignSuccess = true; echo 'Success'; } catch (Exception $e) { //print $e->getMessage(); // mailgun returned an error - we rollback the transaction and remove the number of failed ids from the sent count // consider mainatining a fail count?? $sentCount -= count($campaign1000); $command = Yii::app()->db->createCommand(); $command->update('campaign_contact', array("status" => CampaignContact::STATUS_MAILGUN_ERROR, "processing" => 0), array('in', 'id', $campaign1000IDs)); $transaction->commit(); $msg = print_r($e, true); $msg .= print_r($message, true); mail('*****@*****.**', 'Mailgun error', $msg); } } } if ($campaignSuccess) { $Campaign->status = Campaign::STATUS_HAS_BEEN_RUN; } else { $Campaign->status = Campaign::STATUS_ERROR_SEND; } $Campaign->processing = 0; $Campaign->save(true, array("processing", "status")); print "Completed processing " . $Campaign->id . " \n"; $Campaign->refresh(); print "Status of campaign was " . $Campaign->getStatusText() . "\n\n"; } }
public function actionUploadOutcome($id) { // ensure csv line endings are correctly recognised. ini_set('auto_detect_line_endings', true); $Campaign = Campaign::model()->with(array('outcomes' => array('index' => 'id')))->findByPk($id); if (!sizeof($Campaign->outcomes)) { Yii::app()->user->setFlash('Warning', 'This campaign has no outcomes and as such cannot be updated via file upload.'); } // check for upload file if (sizeof($Campaign->outcomes) && sizeof($_FILES['file']) && strlen($_FILES['file']['tmp_name'])) { // confirm the organisation. if (Yii::app()->user->role < User::ROLE_MANAGER) { $organisation_id = Yii::app()->user->organisation_id; } else { // should have one in post. if (!is_numeric($_POST['organisation_id'])) { throw new CHttpException('400', 'Bad Request - missing organisation_id'); } $organisation_id = (int) $_POST['organisation_id']; } $Organisation = Organisation::model()->findByPk($organisation_id); // check outcome belongs to campaign if (!array_key_exists($_POST['outcome_id'], $Campaign->outcomes)) { // not a valid outcome throw new CHttpException('400', 'Bad Request - invalid outcome.'); } //get the csv file $fh = fopen($_FILES['file']['tmp_name'], "r"); // store number of successes so we can tell rows and individual columns; $successes = []; //loop through the csv file and gather unique ids for ($lines = 0; $data = fgetcsv($fh); $lines++) { if (strlen($data[0])) { $uniqueIDs[] = $data[0]; } } if (!sizeof($uniqueIDs)) { exit('no uniques'); } $CDbCriteria = new CDbCriteria(); $CDbCriteria->join = 'INNER JOIN store2contact ON `t`.warehouse_id = `store2contact`.`contact_warehouse_id`'; $CDbCriteria->addCondition('`t`.`campaign_id` = :campaign_id'); $CDbCriteria->addCondition('`store2contact`.origin_id = :origin_organisation_id'); $CDbCriteria->params = array(':campaign_id' => $Campaign->id, ':origin_organisation_id' => (int) $_POST['organisation_id']); $CDbCriteria->compare('`store2contact`.`origin_unique_id`', $uniqueIDs); $CDbCriteria->index = 'id'; $CampaignContacts = CampaignContact::model()->findAll($CDbCriteria); // update all who match against a campaign contact 2 outcome if (sizeof($CampaignContacts)) { // do update on those rows CampaignContact2Outcome::model()->updateAll(array('outcome' => date("Y-m-d H:i:s")), "campaign_contact_id IN (" . implode(", ", array_keys($CampaignContacts)) . ")\n\t\t\t\t\t\tAND campaign_outcome_id = :campaign_outcome_id\n\t\t\t\t", array(':campaign_outcome_id' => $_POST['outcome_id'])); } // at least something got updated. Yii::app()->user->setFlash('success', sizeof($CampaignContacts) . ' (of ' . $lines . ') rows provided a matching contact to allow outcome update.'); $this->refresh(); } $this->pageTitle = 'Upload Campaign Outcome Users | ' . Yii::app()->name; $this->breadcrumbs = array('Campaigns' => array('index'), $Campaign->name => array('campaign/createUpdate', 'id' => $Campaign->id), 'Upload Campaign Outcome Users'); // show form $this->render('uploadOutcome', array('Campaign' => $Campaign)); }
public function actionCampaignUnsubscribe() { // find them $CampaignContact = CampaignContact::model()->find(array('with' => array('campaign'), 'condition' => "\n\t\t\t\t`t`.`id` = :campaign_contact_id\n\t\t\t\tAND `t`.hash = :campaign_contact_hash\n\t\t\t\tAND `campaign`.id = :campaign_id\n\t\t\t\tAND `campaign`.hash = :campaign_hash\n\t\t\t", 'params' => array('campaign_contact_id' => $_GET['campaign_contact_id'], 'campaign_contact_hash' => $_GET['campaign_contact_hash'], ':campaign_id' => $_GET['campaign_id'], ':campaign_hash' => $_GET['campaign_hash']))); if (is_null($CampaignContact)) { $this->layout = 'vanilla'; $this->pageTitle = '404 not found'; $this->render('unsubscribed', array('title' => 'We couldn\'t find that.', 'message' => 'We weren\'t able to find the page you were looking for. Sorry about that.')); Yii::app()->end(); } // they exist so unsubscribe them $ExistingSuppression = SuppressionList::model()->findByAttributes(array('warehouse_id' => $CampaignContact->warehouse_id)); if (is_null($ExistingSuppression)) { $SuppressionList = new SuppressionList(); $SuppressionList->setAttributes(array('warehouse_id' => $CampaignContact->warehouse_id, 'campaign_id' => $CampaignContact->campaign->id, 'date' => date('Y-m-d H:i:s'), 'type' => 1)); $SuppressionList->save(); } // show them a nice success message $this->layout = 'vanilla'; $this->pageTitle = 'You have been unsubscribed'; $this->render('unsubscribed', array('title' => 'You\'ve unsubscribed successfully.', 'message' => 'We\'re registered your unsubscribe request. You may still receive already queued email correspondance.')); Yii::app()->end(); }
public function actionOpen() { // See http://documentation.mailgun.com/user_manual.html#webhooks // Mail so we know we have received the opened webhook // Set up authorisation $authString = $_POST['timestamp'] . $_POST['token']; $authHash = hash_hmac('sha256', $authString, Yii::app()->params['mailgun']['key']); // Check Auth if ($authHash === $_POST['signature']) { // Huzzah! Authorized HTTP POST from Mailgun $uniques = array(); $StoreModel = new Store(); // Encrypt the email so we can find a match $openedEmailAddress = $StoreModel->encryptEmail($_POST['recipient']); // Look for this email address in store table $StoreRows = Store::model()->with('store2contact')->findAll(array('condition' => 'email = :email', 'params' => array(':email' => $openedEmailAddress))); // collect our warehouse_ids up to match in campaign_contact table. $warehouseIDs = []; if (sizeof($StoreRows)) { // Save 1 suppression row for every instance of the email address in the store table - use store_id foreach ($StoreRows as $Store) { // expired? No store to contact. Skip if (!is_null($Store->store2contact)) { $warehouseIDs[] = $Store->store2contact->contact_warehouse_id; } } // check for campaign_contacts. if (sizeof($warehouseIDs) && is_numeric($_POST['campaign_id']) && is_numeric($_POST['group_id'])) { // it's a bounce of a campaign email. Mark against the row. CampaignContact::model()->updateAll(array('opened' => date('Y-m-d H:i:s', $_POST['timestamp'])), "campaign_id = :campaign_id AND group_id = :group_id AND warehouse_id IN (" . implode(',', array_filter($warehouseIDs)) . ") AND opened IS NULL", array(':campaign_id' => $_POST['campaign_id'], ':group_id' => $_POST['group_id'])); } header("HTTP/1.0 200 Ok"); exit; } else { header("HTTP/1.0 404 Not Found"); exit('Not Found'); } } else { // Go away sleep(5); header("HTTP/1.0 401 Unauthorized"); exit('Unauthorized'); } }