Overview

Packages

  • application
    • commands
    • components
      • actions
      • filters
      • leftWidget
      • permissions
      • sortableWidget
      • util
      • webupdater
      • x2flow
        • actions
        • triggers
      • X2GridView
      • X2Settings
    • controllers
    • models
      • embedded
    • modules
      • accounts
        • controllers
        • models
      • actions
        • controllers
        • models
      • calendar
        • controllers
        • models
      • charts
        • models
      • contacts
        • controllers
        • models
      • docs
        • components
        • controllers
        • models
      • groups
        • controllers
        • models
      • marketing
        • components
        • controllers
        • models
      • media
        • controllers
        • models
      • mobile
        • components
      • opportunities
        • controllers
        • models
      • products
        • controllers
        • models
      • quotes
        • controllers
        • models
      • services
        • controllers
        • models
      • template
        • models
      • users
        • controllers
        • models
      • workflow
        • controllers
        • models
      • x2Leads
        • controllers
        • models
  • None
  • system
    • base
    • caching
    • console
    • db
      • ar
      • schema
    • validators
    • web
      • actions
      • auth
      • helpers
      • widgets
        • captcha
        • pagers
  • zii
    • widgets
      • grid

Classes

  • MarketingController
  • Overview
  • Package
  • Class
  • Tree
  1: <?php
  2: /*****************************************************************************************
  3:  * X2Engine Open Source Edition is a customer relationship management program developed by
  4:  * X2Engine, Inc. Copyright (C) 2011-2016 X2Engine Inc.
  5:  * 
  6:  * This program is free software; you can redistribute it and/or modify it under
  7:  * the terms of the GNU Affero General Public License version 3 as published by the
  8:  * Free Software Foundation with the addition of the following permission added
  9:  * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
 10:  * IN WHICH THE COPYRIGHT IS OWNED BY X2ENGINE, X2ENGINE DISCLAIMS THE WARRANTY
 11:  * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
 12:  * 
 13:  * This program is distributed in the hope that it will be useful, but WITHOUT
 14:  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 15:  * FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
 16:  * details.
 17:  * 
 18:  * You should have received a copy of the GNU Affero General Public License along with
 19:  * this program; if not, see http://www.gnu.org/licenses or write to the Free
 20:  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 21:  * 02110-1301 USA.
 22:  * 
 23:  * You can contact X2Engine, Inc. P.O. Box 66752, Scotts Valley,
 24:  * California 95067, USA. or at email address contact@x2engine.com.
 25:  * 
 26:  * The interactive user interfaces in modified source and object code versions
 27:  * of this program must display Appropriate Legal Notices, as required under
 28:  * Section 5 of the GNU Affero General Public License version 3.
 29:  * 
 30:  * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
 31:  * these Appropriate Legal Notices must retain the display of the "Powered by
 32:  * X2Engine" logo. If the display of the logo is not reasonably feasible for
 33:  * technical reasons, the Appropriate Legal Notices must display the words
 34:  * "Powered by X2Engine".
 35:  *****************************************************************************************/
 36: 
 37: /**
 38:  * Controller to handle creating and mailing campaigns.
 39:  *
 40:  * @package application.modules.marketing.controllers
 41:  */
 42: class MarketingController extends x2base {
 43:     
 44:     public $modelClass = 'Campaign';
 45: 
 46:     public function behaviors(){
 47:         return array_merge(parent::behaviors(), array(
 48:             'CampaignMailingBehavior' => array('class' => 'application.modules.marketing.components.CampaignMailingBehavior'),
 49:             'ResponseBehavior' => array('class' => 'application.components.ResponseBehavior', 'isConsole' => false,'errorCode'=>200),
 50:         ));
 51:     }
 52: 
 53:     public function accessRules(){
 54:         return array(
 55:             array('allow', // allow all users
 56:                 'actions' => array('click', 'doNotEmailLinkClick'),
 57:                 'users' => array('*'),
 58:             ),
 59:             array('allow', // allow authenticated user to perform the following actions
 60:                 'actions' => array(
 61:                     'index', 'view', 'create', 'createFromTag', 'update', 'search', 'delete', 
 62:                     'launch', 
 63:                     'toggle', 'complete', 'getItems', 'inlineEmail', 'mail', 'deleteWebForm',
 64:                     'webleadForm'),
 65:                 'users' => array('@'),
 66:             ),
 67:             array('allow', // allow admin user to perform 'admin' action
 68:                 'actions' => array('admin'),
 69:                 'users' => array('admin'),
 70:             ),
 71:             array('deny', // deny all users
 72:                 'users' => array('*'),
 73:             ),
 74:         );
 75:     }
 76: 
 77:     public function actions(){
 78:         return array_merge(parent::actions(), array(
 79:             'webleadForm' => array(
 80:                 'class' => 'CreateWebFormAction',
 81:             ),
 82:             'inlineEmail' => array(
 83:                 'class' => 'application.modules.marketing.components.actions.TestEmailAction',
 84:             ),
 85:         ));
 86:     }
 87: 
 88:     /**
 89:      * Deletes a web form record with the specified id 
 90:      * @param int $id
 91:      */
 92:     public function actionDeleteWebForm ($id) {
 93:         $model = WebForm::model ()->findByPk ($id); 
 94:         $name = $model->name;
 95:         $success = false;
 96: 
 97:         if ($model) {
 98:             $success = $model->delete ();
 99:         }
100:         AuxLib::ajaxReturn (
101:             $success,  
102:             Yii::t('app', "Deleted '$name'"),
103:             Yii::t('app', 'Unable to delete web form')
104:         );
105:     }
106: 
107:     
108: 
109:     /**
110:      * Returns a JSON array of the names of all campaigns filtered by a search term.
111:      *
112:      * @return string A JSON array of strings
113:      */
114:     public function actionGetItems($modelType){
115:         $term = $_GET['term'].'%';
116:          
117:             X2LinkableBehavior::getItems ($term);
118:          
119:     }
120:     
121: 
122: 
123:     /**
124:      * Displays a particular model.
125:      *
126:      * @param integer $id the ID of the model to be displayed
127:      */
128:     public function actionView($id){
129:         $model = $this->loadModel($id);
130:         if (!$this->checkPermissions($model, 'view')) $this->denied ();
131: 
132:         if (isset($_GET['ajax']) && $_GET['ajax'] == 'campaign-grid') {
133:             $this->renderPartial('campaignGrid', array('model' => $model));
134:             return;
135:         }
136: 
137:         if(!isset($model)){
138:             Yii::app()->user->setFlash(
139:                 'error', Yii::t('app', 'The requested page does not exist.'));
140:             $this->redirect(array('index'));
141:         }
142: 
143:         if(isset($model->list)){
144:             //set this as the list we are viewing, for use by vcr controls
145:             Yii::app()->user->setState('contacts-list', $model->list->id);
146:         }
147: 
148:         // add campaign to user's recent item list
149:         User::addRecentItem('p', $id, Yii::app()->user->getId()); 
150: 
151:         $this->view($model, 'marketing');
152:     }
153: 
154:     /**
155:      * Displays the content field (email template) for a particular model.
156:      *
157:      * @param integer $id the ID of the model to be displayed
158:      */
159:     public function actionViewContent($id){
160:         $model = $this->loadModel($id);
161: 
162:         if(!isset($model)){
163:             Yii::app()->user->setFlash('error', Yii::t('app', 'The requested page does not exist.'));
164:             $this->redirect(array('index'));
165:         }
166: 
167:         echo $model->content;
168:     }
169: 
170:     /**
171:      * Override of {@link CommonControllerBehavior::loadModel()}; expected
172:      * behavior is in this case deference to the campaign model's
173:      * {@link Campagin::load()} function.
174:      *
175:      * @param type $id
176:      * @return type
177:      */
178:     public function loadModel($id){
179:         return Campaign::load($id);
180:     }
181: 
182:     /**
183:      * Creates a new model.
184:      * If creation is successful, the browser will be redirected to the 'view' page.
185:      */
186:     public function actionCreate(){
187:         $model = new Campaign;
188:         $model->type = 'Email'; //default choice for now
189: 
190:         if(isset($_POST['Campaign'])){
191:             $model->setX2Fields($_POST['Campaign']);
192: 
193:             $model->content = Fields::getPurifier()->purify($model->content);
194:             $model->content = Formatter::restoreInsertableAttributes($model->content);
195:             $model->createdBy = Yii::app()->user->getName();
196:             if($model->save()){
197:                 if(isset($_POST['AttachmentFiles'])){
198:                     if(isset($_POST['AttachmentFiles']['id'])){
199:                         foreach($_POST['AttachmentFiles']['id'] as $mediaId){
200:                             $attachment = new CampaignAttachment;
201:                             $attachment->campaign = $model->id;
202:                             $attachment->media = $mediaId;
203:                             $attachment->save();
204:                         }
205:                     }
206:                 }
207:                 $this->redirect(array('view', 'id' => $model->id));
208:             }
209:         }elseif(isset($_GET['Campaign'])){
210:             //preload the create form with query params
211:             $model->setAttributes($_GET['Campaign']);
212:             $model->setX2Fields($_GET['Campaign']);
213:         }
214: 
215:         $this->render('create', array('model' => $model));
216:     }
217: 
218:     /**
219:      * Create a campaign for all contacts with a certain tag.
220:      *
221:      * This action will create and save the campaign and redirect the user to
222:      * edit screen to fill in the email message, etc.  It is intended to provide
223:      * a fast workflow from tags to campaigns.
224:      *
225:      * @param string $tag
226:      */
227:     public function actionCreateFromTag($tag){
228:         //enusre tag sanity
229:         if(empty($tag) || strlen(trim($tag)) == 0){
230:             Yii::app()->user->setFlash('error', Yii::t('marketing', 'Invalid tag value'));
231:             $this->redirect(Yii::app()->request->getUrlReferrer());
232:         }
233: 
234:         //ensure sacred hash
235:         if(substr($tag, 0, 1) != '#'){
236:             $tag = '#'.$tag;
237:         }
238: 
239:         //only works for contacts
240:         $modelType = 'Contacts';
241:         $now = time();
242: 
243:         //get all contact ids from tags
244:         $ids = Yii::app()->db->createCommand()
245:                 ->select('itemId')
246:                 ->from('x2_tags')
247:                 ->where('type=:type AND tag=:tag')
248:                 ->group('itemId')
249:                 ->order('itemId ASC')
250:                 ->bindValues(array(':type' => $modelType, ':tag' => $tag))
251:                 ->queryColumn();
252: 
253:         //create static list
254:         $list = new X2List;
255:         $list->name = Yii::t('marketing', 'Contacts for tag').' '.$tag;
256:         $list->modelName = $modelType;
257:         $list->type = 'campaign';
258:         $list->count = count($ids);
259:         $list->visibility = 1;
260:         $list->assignedTo = Yii::app()->user->getName();
261:         $list->createDate = $now;
262:         $list->lastUpdated = $now;
263: 
264:         //create campaign
265:         $campaign = new Campaign;
266:         $campaign->name = Yii::t('marketing', 'Mailing for tag').' '.$tag;
267:         $campaign->type = 'Email';
268:         $campaign->visibility = 1;
269:         $campaign->assignedTo = Yii::app()->user->getName();
270:         $campaign->createdBy = Yii::app()->user->getName();
271:         $campaign->updatedBy = Yii::app()->user->getName();
272:         $campaign->createDate = $now;
273:         $campaign->lastUpdated = $now;
274: 
275:         $transaction = Yii::app()->db->beginTransaction();
276:         try{
277:             if(!$list->save())
278:                 throw new Exception(array_shift(array_shift($list->getErrors())));
279:             $campaign->listId = $list->nameId;
280:             if(!$campaign->save())
281:                 throw new Exception(array_shift(array_shift($campaign->getErrors())));
282: 
283:             foreach($ids as $id){
284:                 $listItem = new X2ListItem;
285:                 $listItem->listId = $list->id;
286:                 $listItem->contactId = $id;
287:                 if(!$listItem->save())
288:                     throw new Exception(array_shift(array_shift($listItem->getErrors())));
289:             }
290: 
291:             $transaction->commit();
292:             $this->redirect($this->createUrl('update', array('id' => $campaign->id)));
293:         }catch(Exception $e){
294:             $transaction->rollBack();
295:             Yii::app()->user->setFlash('error', Yii::t('marketing', 'Could not create mailing').': '.$e->getMessage());
296:             $this->redirect(Yii::app()->request->getUrlReferrer());
297:         }
298:     }
299: 
300:     /**
301:      * Updates a particular model.
302:      * If update is successful, the browser will be redirected to the 'view' page.
303:      *
304:      * @param integer $id the ID of the model to be updated
305:      */
306:     public function actionUpdate($id){
307:         $model = $this->loadModel($id);
308: 
309:         if(!isset($model)){
310:             Yii::app()->user->setFlash('error', Yii::t('app', 'The requested page does not exist.'));
311:             $this->redirect(array('index'));
312:         }
313: 
314:         if(isset($_POST['Campaign'])){
315:             $oldAttributes = $model->attributes;
316:             $model->setX2Fields($_POST['Campaign']);
317:             $model->content = Fields::getPurifier()->purify($model->content);
318:             $model->content = Formatter::restoreInsertableAttributes($model->content);
319: 
320:             if($model->save()){
321:                 CampaignAttachment::model()->deleteAllByAttributes(array('campaign' => $model->id));
322:                 if(isset($_POST['AttachmentFiles'])){
323:                     if(isset($_POST['AttachmentFiles']['id'])){
324:                         foreach($_POST['AttachmentFiles']['id'] as $mediaId){
325:                             $attachment = new CampaignAttachment;
326:                             $attachment->campaign = $model->id;
327:                             $attachment->media = $mediaId;
328:                             $attachment->save();
329:                         }
330:                     }
331:                 }
332:                 $this->redirect(array('view', 'id' => $model->id));
333:             }
334:         }
335: 
336:         $this->render('update', array('model' => $model));
337:     }
338: 
339:     /**
340:      * Deletes a particular model.
341:      * If deletion is successful, the browser will be redirected to the 'index' page.
342:      *
343:      * @param integer $id the ID of the model to be deleted
344:      */
345:     public function actionDelete($id){
346:         if(Yii::app()->request->isPostRequest){
347:             $model = $this->loadModel($id);
348: 
349:             if(!isset($model)){
350:                 Yii::app()->user->setFlash('error', Yii::t('app', 'The requested page does not exist.'));
351:                 $this->redirect(array('index'));
352:             }
353:             // now in X2ChangeLogBehavior
354:             // $event=new Events;
355:             // $event->type='record_deleted';
356:             // $event->associationType=$this->modelClass;
357:             // $event->associationId=$model->id;
358:             // $event->text=$model->name;
359:             // $event->user=Yii::app()->user->getName();
360:             // $event->save();
361:             $list = $model->list;
362:             if(isset($list) && $list->type == "campaign")
363:                 $list->delete();
364:             // $this->cleanUpTags($model);  // now in TagBehavior
365:             $model->delete();
366: 
367:             // if AJAX request (triggered by deletion via admin grid view), we should not redirect the browser
368:             if(!isset($_GET['ajax']))
369:                 $this->redirect(isset($_POST['returnUrl']) ? $_POST['returnUrl'] : array('index'));
370:         } else{
371:             Yii::app()->user->setFlash('error', Yii::t('app', 'Invalid request. Please do not repeat this request again.'));
372:             $this->redirect(array('index'));
373:         }
374:     }
375: 
376:     /**
377:      * Lists all models.
378:      */
379:     public function actionIndex(){
380:         $model = new Campaign('search');
381:         $this->render('index', array('model' => $model));
382:     }
383: 
384:     public function actionAdmin(){
385:         $this->redirect('index');
386:     }
387: 
388:     /**
389:      * Launches the specified campaign, activating it for mailing
390:      *
391:      * When a campaign is created, it is specified with an existing contact list.
392:      * When the campaign is lauched, this list is replaced with a duplicate to prevent
393:      * the original from being modified, and to allow campaign specific information to
394:      * be saved in the list.  This includes the email send time, and the times when a
395:      * contact has opened the mail or unsubscribed from the list.
396:      *
397:      * @param integer $id ID of the campaign to launch
398:      */
399:     public function actionLaunch($id){
400:         $campaign = $this->loadModel($id);
401: 
402:         if(!isset($campaign)){
403:             Yii::app()->user->setFlash('error', Yii::t('app', 'The requested page does not exist.'));
404:             $this->redirect(array('index'));
405:         }
406: 
407:         if(!isset($campaign->list)){
408:             Yii::app()->user->setFlash('error', Yii::t('marketing', 'Contact List cannot be blank.'));
409:             $this->redirect(array('view', 'id' => $id));
410:         }
411: 
412:         if(empty($campaign->subject) && $campaign->type === 'Email'){
413:             Yii::app()->user->setFlash('error', Yii::t('marketing', 'Subject cannot be blank.'));
414:             $this->redirect(array('view', 'id' => $id));
415:         }
416: 
417:         if($campaign->launchDate != 0 && $campaign->launchDate < time()){
418:             Yii::app()->user->setFlash('error', Yii::t('marketing', 'The campaign has already been launched.'));
419:             $this->redirect(array('view', 'id' => $id));
420:         }
421: 
422:         if(($campaign->list->type == 'dynamic' && 
423:             X2Model::model($campaign->list->modelName)
424:                 ->count($campaign->list->queryCriteria()) < 1) || 
425:             ($campaign->list->type != 'dynamic' && count($campaign->list->listItems) < 1)){
426: 
427:             Yii::app()->user->setFlash('error', Yii::t('marketing', 'The contact list is empty.'));
428:             $this->redirect(array('view', 'id' => $id));
429:         }
430: 
431:         //Duplicate the list for campaign tracking, leave original untouched
432:         //only if the list is not already a campaign list
433:         if($campaign->list->type != "campaign"){
434:             $newList = $campaign->list->staticDuplicate();
435:             if(!isset($newList)){
436:                 Yii::app()->user->setFlash('error', Yii::t('marketing', 'The contact list is empty.'));
437:                 $this->redirect(array('view', 'id' => $id));
438:             }
439:             $newList->type = 'campaign';
440:             if($newList->save()) {
441:                 $campaign->list = $newList;
442:                 $campaign->listId = $newList->nameId;
443:             } else {
444:                 Yii::app()->user->setFlash('error', Yii::t('marketing', 'Failed to save temporary list.'));
445:             }
446:         }
447: 
448:         $campaign->launchDate = time();
449:         $campaign->save();
450: 
451:         Yii::app()->user->setFlash('success', Yii::t('marketing', 'Campaign launched'));
452:         $this->redirect(array('view', 'id' => $id, 'launch' => true));
453:     }
454: 
455:     /**
456:      * Deactivate a campaign to halt mailings, or resume paused campaign
457:      *
458:      * @param integer $id The ID of the campaign to toggle
459:      */
460:     public function actionToggle($id){
461:         $campaign = $this->loadModel($id);
462: 
463:         if(!isset($campaign)){
464:             Yii::app()->user->setFlash('error', Yii::t('app', 'The requested page does not exist.'));
465:             $this->redirect(array('index'));
466:         }
467: 
468:         $campaign->active = $campaign->active ? 0 : 1;
469:         $campaign->save();
470:         $message = $campaign->active ? Yii::t('marketing', 'Campaign resumed') : Yii::t('marketing', 'Campaign paused');
471:         Yii::app()->user->setFlash('notice', Yii::t('app', $message));
472:         $this->redirect(array('view', 'id' => $id, 'launch' => $campaign->active));
473:     }
474: 
475:     /**
476:      * Forcibly complete a campaign despite any unsent mail
477:      *
478:      * @param integer $id The ID of the campaign to complete
479:      */
480:     public function actionComplete($id){
481:         $campaign = $this->loadModel($id);
482: 
483:         if(!isset($campaign)){
484:             Yii::app()->user->setFlash('error', Yii::t('app', 'The requested page does not exist.'));
485:             $this->redirect(array('index'));
486:         }
487: 
488:         $campaign->active = 0;
489:         $campaign->complete = 1;
490:         $campaign->save();
491:         $message = Yii::t('marketing', 'Campaign complete.');
492:         Yii::app()->user->setFlash('notice', Yii::t('app', $message));
493:         $this->redirect(array('view', 'id' => $id));
494:     }
495: 
496:     /**
497:      * Sends an individual email to an item in a campaign/newsletter list.
498:      *
499:      * @param type $campaignId
500:      * @param type $itemId
501:      */
502:     public function actionMailIndividual($campaignId,$itemId) {
503:         $this->itemId = $itemId;
504:         $this->campaign = Campaign::model()->findByPk($campaignId);
505:         $email = $this->recipient->email;
506:         if($this->campaign instanceof Campaign && $this->listItem instanceof X2ListItem) {
507:             $this->sendIndividualMail();
508:             $this->response['fullStop'] = $this->fullStop;
509:             $status = $this->status;
510:             // Actual SMTP (or elsewise) delivery error that should stop the batch:
511:             $error = ($status['code']!=200 && $this->undeliverable) || $this->fullStop;
512:             $this->response['status'] = $this->status;
513:             $this->respond($status['message'],$error);
514:         } else {
515:             $this->respond(Yii::t('marketing','Specified campaign does not exist.'),1);
516:         }
517:     }
518: 
519:     public function actionDoNotEmailLinkClick ($x2_key) {
520:         $contact = Contacts::model ()->findByAttributes (array (
521:             'trackingKey' => $x2_key,
522:         ));
523:         if ($contact !== null) {
524:             $contact->doNotEmail = true;
525:             if ($contact->update ()) {
526:                 if (Yii::app()->settings->doNotEmailPage) {
527:                     echo Yii::app()->settings->doNotEmailPage;
528:                 } else {
529:                     echo Admin::getDoNotEmailDefaultPage ();
530:                 }
531:             }
532:         }
533: 
534:     }
535: 
536:     /**
537:      * Track when an email is viewed, a link is clicked, or the recipient unsubscribes
538:      *
539:      * Campaign emails include an img tag to a blank image to track when the message was opened,
540:      * an unsubscribe link, and converted links to track when a recipient clicks a link.
541:      * All those links are handled by this action.
542:      *
543:      * @param integer $uid The unique id of the recipient
544:      * @param string $type 'open', 'click', or 'unsub'
545:      * @param string $url For click types, this is the urlencoded URL to redirect to
546:      * @param string $email For unsub types, this is the urlencoded email address
547:      *  of the person unsubscribing
548:      */
549:     public function actionClick($uid, $type, $url = null, $email = null){
550:         $now = time();
551:         $item = CActiveRecord::model('X2ListItem')
552:             ->with('contact', 'list')->findByAttributes(array('uniqueId' => $uid));
553: 
554:         // It should never happen that we have a list item without a campaign,
555:         // but it WILL happen on any old db where x2_list_items does not cascade on delete
556:         // we can't track anything if the listitem was deleted, but at least prevent breaking links
557:         if($item === null || $item->list->campaign === null){
558:             if($type == 'click'){
559:                 // campaign redirect link click
560:                 $this->redirect(htmlspecialchars_decode($url, ENT_NOQUOTES));
561:             }elseif($type == 'open'){
562:                 //return a one pixel transparent gif
563:                 header('Content-Type: image/gif');
564:                 echo base64_decode('R0lGODlhAQABAIABAP///wAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==');
565:             }elseif($type == 'unsub' && !empty($email)){
566:                 Contacts::model()
567:                     ->updateAll(
568:                         array('doNotEmail' => true), 'email=:email', array(':email' => $email));
569:                 X2ListItem::model()
570:                     ->updateAll(
571:                         array('unsubscribed' => time()), 
572:                         'emailAddress=:email AND unsubscribed=0', array('email' => $email));
573:                 $message = Yii::t('marketing', 'You have been unsubscribed');
574:                 echo '<html><head><title>'.$message.'</title></head><body>'.$message.
575:                     '</body></html>';
576:             }
577:             return;
578:         }
579: 
580:         $contact = $item->contact;
581:         $list = $item->list;
582: 
583:         $event = new Events;
584:         $notif = new Notification;
585: 
586:         $action = new Actions;
587:         $action->completeDate = $now;
588:         $action->complete = 'Yes';
589:         $action->updatedBy = 'API';
590:         $skipActionEvent = true;
591: 
592:         if($contact !== null){
593:             $skipActionEvent = false;
594:             if($email === null)
595:                 $email = $contact->email;
596: 
597:             $action->associationType = 'contacts';
598:             $action->associationId = $contact->id;
599:             $action->associationName = $contact->name;
600:             $action->visibility = $contact->visibility;
601:             $action->assignedTo = $contact->assignedTo;
602: 
603:             $event->associationId = $action->associationId;
604:             $event->associationType = 'Contacts';
605: 
606:             if($action->assignedTo !== '' && $action->assignedTo !== 'Anyone'){
607:                 $notif->user = $contact->assignedTo;
608:                 $notif->modelType = 'Contacts';
609:                 $notif->modelId = $contact->id;
610:                 $notif->createDate = $now;
611:                 $notif->value = $item->list->campaign->getLink();
612:             }
613:         }elseif($list !== null){
614:             $action = new Actions;
615:             $action->type = 'note';
616:             $action->createDate = $now;
617:             $action->lastUpdated = $now;
618:             $action->completeDate = $now;
619:             $action->complete = 'Yes';
620:             $action->updatedBy = 'admin';
621: 
622:             $action->associationType = 'X2List';
623:             $action->associationId = $list->id;
624:             $action->associationName = $list->name;
625:             $action->visibility = $list->visibility;
626:             $action->assignedTo = $list->assignedTo;
627:         }
628: 
629:         if($type == 'unsub'){
630:             $item->unsubscribe();
631: 
632:             // find any weblists associated with the email address and create unsubscribe actions 
633:             // for each of them
634:             $sql = 
635:                 'SELECT t.* 
636:                 FROM x2_lists as t 
637:                 JOIN x2_list_items as li ON t.id=li.listId 
638:                 WHERE li.emailAddress=:email AND t.type="weblist";';
639:             $weblists = Yii::app()->db->createCommand($sql)
640:                 ->queryAll(true, array('email' => $email));
641:             foreach($weblists as $weblist){
642:                 $weblistAction = new Actions();
643:                 $weblistAction->disableBehavior('changelog');
644:                 //$weblistAction->id = 0; // this causes primary key contraint violation errors
645:                 $weblistAction->isNewRecord = true;
646:                 $weblistAction->type = 'email_unsubscribed';
647:                 $weblistAction->associationType = 'X2List';
648:                 $weblistAction->associationId = $weblist['id'];
649:                 $weblistAction->associationName = $weblist['name'];
650:                 $weblistAction->visibility = $weblist['visibility'];
651:                 $weblistAction->assignedTo = $weblist['assignedTo'];
652:                 $weblistAction->actionDescription = Yii::t('marketing', 'Campaign').': '.
653:                     $item->list->campaign->name."\n\n".$email." ".
654:                     Yii::t('marketing', 'has unsubscribed').".";
655:                 $weblistAction->save();
656:             }
657: 
658:             $action->type = 'email_unsubscribed';
659:             $notif->type = 'email_unsubscribed';
660: 
661:             if($contact === null) {
662:                 $action->actionDescription = Yii::t('marketing', 'Campaign').': '.
663:                 $item->list->campaign->name."\n\n".$item->emailAddress.' '.
664:                 Yii::t('marketing', 'has unsubscribed').".";
665:             } else {
666:                 $action->actionDescription = Yii::t('marketing', 'Campaign').': '.
667:                     $item->list->campaign->name."\n\n".
668:                     Yii::t('marketing', 'Contact has unsubscribed').".\n".
669:                     Yii::t('marketing', '\'Do Not Email\' has been set').".";
670:             }
671: 
672:             $message = Yii::t('marketing', 'You have been unsubscribed');
673:             echo '<html><head><title>'.$message.'</title></head><body>'.$message.'</body></html>';
674:         } elseif($type == 'open'){
675:             //return a one pixel transparent gif
676:             header('Content-Type: image/gif');
677:             echo base64_decode('R0lGODlhAQABAIABAP///wAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==');
678:             // Check if it has been marked as opened already, or if the contact
679:             // no longer exists. If so, exit; nothing more need be done.
680:             if($item->opened != 0)
681:                 Yii::app()->end();
682:             // This needs to happen before the skip option to accomodate the case of newsletters
683:             $item->markOpened(); 
684:             if($skipActionEvent)
685:                 Yii::app()->end();
686:             $action->disableBehavior('changelog');
687:             $action->type = 'campaignEmailOpened';
688:             $event->type = 'email_opened';
689:             $notif->type = 'email_opened';
690:             $event->save();
691:             if($contact === null) {
692:                 $action->actionDescription = Yii::t('marketing', 'Campaign').': '.
693:                     $item->list->campaign->name."\n\n".$item->emailAddress.' '.
694:                     Yii::t('marketing', 'has opened the email').".";
695:             } else {
696:                 $action->actionDescription = Yii::t('marketing', 'Campaign').': '.
697:                     $item->list->campaign->name."\n\n".
698:                     Yii::t('marketing', 'Contact has opened the email').".";
699:             }
700:         } elseif($type == 'click'){
701:             // redirect link click
702:             $item->markClicked($url);
703: 
704:             $action->type = 'email_clicked';
705:             $notif->type = 'email_clicked';
706: 
707:             if($contact === null) {
708:                 $action->actionDescription = Yii::t('marketing', 'Campaign').': '.
709:                     $item->list->campaign->name."\n\n".
710:                     Yii::t('marketing', 'Contact has clicked a link').":\n".urldecode($url);
711:             } else {
712:                 $action->actionDescription = Yii::t('marketing', 'Campaign').': '.
713:                     $item->list->campaign->name."\n\n".$item->emailAddress.' '.
714:                     Yii::t('marketing', 'has clicked a link').":\n".urldecode($url);
715:             }
716:             $this->redirect(htmlspecialchars_decode($url, ENT_NOQUOTES));
717:         }
718: 
719:         $action->save();
720:         // if any of these hasn't been fully configured
721:         $notif->save();  // it will simply not validate and not be saved
722:     }
723: 
724:     public function actionRemoveWebLeadFormCustomHtml () {
725:         if(!empty($_POST) && !empty ($_POST['id'])) {
726:             $model = WebForm::model()->findByPk ($_POST['id']);
727:             if ($model) {
728:                 $model->header = '';
729:                 if ($model->save ()) {
730:                     echo CJSON::encode (
731:                         array ('success', $model->attributes));
732:                     return;
733:                 }
734:             }
735:         }
736:         echo CJSON::encode (
737:             array ('error', Yii::t('marketing', 'Custom HTML could not be removed.')));
738:     }
739: 
740:     public function actionSaveWebLeadFormCustomHtml () {
741:         if(!empty($_POST) && !empty ($_POST['id']) && !empty ($_POST['html'])){
742:             $model = WebForm::model()->findByPk ($_POST['id']);
743:             if ($model) {
744:                 $model->header = $_POST['html'];
745:                 if ($model->save ()) {
746:                     echo CJSON::encode (array ('success', $model->attributes));
747:                     return;
748:                 }
749:             }
750:         }
751:         echo CJSON::encode (
752:             array ('error', Yii::t('marketing', 'Custom HTML could not be saved.')));
753:     }
754: 
755:     /**
756:      * Get the web tracker code to insert into your website
757:      */
758:     public function actionWebTracker(){
759:         $admin = Yii::app()->settings;
760:         if(isset($_POST['Admin']['enableWebTracker'], $_POST['Admin']['webTrackerCooldown'])){
761:             $admin->enableWebTracker = $_POST['Admin']['enableWebTracker'];
762:             $admin->webTrackerCooldown = $_POST['Admin']['webTrackerCooldown'];
763:             
764:             $admin->save();
765:         }
766:         $this->render('webTracker', array('admin' => $admin));
767:     }
768: 
769:     
770: 
771:     /**
772:      * Create a menu for Marketing
773:      * @param array Menu options to remove
774:      * @param X2Model Model object passed to the view
775:      * @param array Additional menu parameters
776:      */
777:     public function insertMenu($selectOptions = array(), $model = null, $menuParams = null) {
778:         $Contact = Modules::displayName(false, "Contacts");
779:         $modelId = isset($model) ? $model->id : 0;
780:         $marketingAdmin = Yii::app()->user->checkAccess ('MarketingAdminAccess');
781: 
782:         /**
783:          * To show all options:
784:          * $menuOptions = array(
785:          *     'all', 'create', 'view', 'edit', 'delete', 'lists', 'import', 'export',
786:          *     'newsletters', 'weblead', 'webtracker', 'x2flow', 'email',
787:          * );
788:          */
789:         
790: 
791:         $menuItems = array(
792:             array(
793:                 'name'=>'all',
794:                 'label'=>Yii::t('marketing','All Campaigns'),
795:                 'url'=>array('index')
796:             ),
797:             array(
798:                 'name'=>'create',
799:                 'label'=>Yii::t('marketing','Create Campaign'),
800:                 'url'=>array('create')
801:             ),
802:             RecordViewLayoutManager::getViewActionMenuListItem ($modelId),
803:             array(
804:                 'name'=>'viewAnon',
805:                 'label' => Yii::t('module', 'View'),
806:                 'url'=>array('anonContactView', 'id' => $modelId),
807:             ),
808:             array(
809:                 'name'=>'edit',
810:                 'label' => Yii::t('module', 'Update'),
811:                 'url' => array('update', 'id' => $modelId)
812:             ),
813:             array(
814:                 'name'=>'delete',
815:                 'label' => Yii::t('module', 'Delete'),
816:                 'url' => '#',
817:                 'linkOptions' => array(
818:                     'submit' => array('delete', 'id' => $modelId),
819:                     'confirm' => Yii::t('app', 'Are you sure you want to delete this item?'))
820:             ),
821:             array(
822:                 'name' => 'deleteAnon',
823:                 'label' => Yii::t('contacts', 'Delete'), 
824:                 'url' => '#', 
825:                 'linkOptions' => array(
826:                     'submit' => array('/marketing/anonContactDelete', 'id' => $modelId),
827:                     'confirm' => 'Are you sure you want to delete this anonymous contact?')
828:             ),
829:             array(
830:                 'name'=>'lists',
831:                 'label'=>Yii::t('contacts','{module} Lists', array('{module}'=>$Contact)),
832:                 'url'=>array('/contacts/contacts/lists')),
833:             array(
834:                 'name'=>'import',
835:                 'label'=>Yii::t('marketing', 'Import Campaigns'),
836:                 'url'=>array('admin/importModels', 'model'=>'Campaign'),
837:             ),
838:             array(
839:                 'name'=>'export',
840:                 'label'=>Yii::t('marketing', 'Export Campaigns'),
841:                 'url'=>array('admin/exportModels', 'model'=>'Campaign'),
842:             ),
843:             array(
844:                 'name'=>'newsletters',
845:                 'label' => Yii::t('marketing', 'Newsletters'),
846:                 'url' => array('/marketing/weblist/index'),
847:                 'visible' => (Yii::app()->contEd('pro'))
848:             ),
849:             array(
850:                 'name'=>'weblead',
851:                 'label' => Yii::t('marketing', 'Web Lead Form'),
852:                 'url' => array('webleadForm')
853:             ),
854:             array(
855:                 'name'=>'webtracker',
856:                 'label' => Yii::t('marketing', 'Web Tracker'),
857:                 'url' => array('webTracker'),
858:                 'visible' => (Yii::app()->contEd('pro'))
859:             ),
860:         
861:             array(
862:                 'name'=>'x2flow',
863:                 'label' => Yii::t('app', 'X2Workflow'),
864:                 'url' => array('/studio/flowIndex'),
865:                 'visible' => (Yii::app()->contEd('pro'))
866:             ),
867:             array(
868:                 'name'=>'email',
869:                 'label' => Yii::t('app', 'Send Email'), 'url' => '#',
870:                 'linkOptions' => array('onclick' => 'toggleEmailForm(); return false;')
871:             ),
872:             RecordViewLayoutManager::getEditLayoutActionMenuListItem (),
873:         );
874: 
875:         $this->prepareMenu($menuItems, $selectOptions);
876:         $this->actionMenu = $this->formatMenu($menuItems, $menuParams);
877:     }
878: 
879: }
880: 
X2CRM Documentation API documentation generated by ApiGen 2.8.0