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

  • ActionMetaData
  • ActionText
  • Admin
  • AmorphousModel
  • ApiHook
  • APIModel
  • ChartSetting
  • ContactForm
  • ContactList
  • Credentials
  • Criteria
  • Dropdowns
  • Events
  • EventsData
  • Fields
  • FormLayout
  • Imports
  • InlineEmail
  • LeadRouting
  • Locations
  • LoginForm
  • Maps
  • Modules
  • Notes
  • Notification
  • PhoneNumber
  • Profile
  • Record
  • Relationships
  • Roles
  • RoleToPermission
  • RoleToUser
  • RoleToWorkflow
  • Rules
  • Session
  • SessionLog
  • Social
  • Tags
  • TempFile
  • Tips
  • Tours
  • TrackEmail
  • TriggerLog
  • URL
  • ViewLog
  • Widgets
  • X2List
  • X2ListCriterion
  • X2ListItem
  • X2Model
  • 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:  * This is the model class for table "x2_lists".
 39:  *
 40:  * @package application.models
 41:  */
 42: class X2List extends X2Model {
 43: 
 44:     /**
 45:      * Attribute name, comparison operator and comparison value arrays for
 46:      * criteria generation.
 47:      *
 48:      * @var type
 49:      */
 50:     public $criteriaInput;
 51: 
 52:     public $supportsWorkflow = false;
 53: 
 54:     private $_itemModel = null;
 55: 
 56:     private $_itemFields = array();
 57: 
 58:     private $_itemAttributeLabels = array();
 59: 
 60:     public static function model($className = __CLASS__){
 61:         return parent::model($className);
 62:     }
 63: 
 64:     /**
 65:      * Returns the name of the associated database table
 66:      * @return string the associated database table name
 67:      */
 68:     public function tableName(){
 69:         return 'x2_lists';
 70:     }
 71: 
 72:     public function behaviors(){
 73:         return array(
 74:             'ERememberFiltersBehavior' => array(
 75:                 'class' => 'application.components.ERememberFiltersBehavior',
 76:                 'defaults' => array(),
 77:                 'defaultStickOnClear' => false
 78:             ),
 79:             'X2LinkableBehavior' => array(
 80:                 'class' => 'X2LinkableBehavior',
 81:                 'baseRoute' => '/contacts/contacts/list',
 82:                 'autoCompleteSource' => '/contacts/contacts/getLists',
 83:             ),
 84:             'X2PermissionsBehavior' => array(
 85:                 'class' => 'application.components.permissions.'.Yii::app()->params->modelPermissions
 86:             ),
 87:             'X2FlowTriggerBehavior' => array('class' => 'X2FlowTriggerBehavior'),
 88:         );
 89:     }
 90: 
 91:     public function rules(){
 92:         // NOTE: you should only define rules for those attributes that
 93:         // will receive user inputs.
 94:         return array(
 95:             array('name, createDate, lastUpdated, modelName', 'required'),
 96:             array('id, count, visibility, createDate, lastUpdated', 'numerical', 'integerOnly' => true),
 97:             array('name, modelName', 'length', 'max' => 100),
 98:             array('description', 'length', 'max' => 250),
 99:             array('assignedTo, type, logicType', 'length', 'max' => 20),
100:             // The following rule is used by search().
101:             // Please remove those attributes that should not be searched.
102:             array('id, assignedTo, name, modelName, count, visibility, description, type, createDate, lastUpdated', 'safe', 'on' => 'search'),
103:         );
104:     }
105: 
106:     public function relations(){
107:         // NOTE: you may need to adjust the relation name and the related
108:         // class name for the relations automatically generated below.
109:         return array(
110:             'listItems' => array(self::HAS_MANY, 'X2ListItem', 'listId'),
111:             'campaign' => array(self::HAS_ONE, 'Campaign', array('listId' => 'nameId')),
112:             'criteria' => array(self::HAS_MANY,'X2ListCriterion','listId'),
113:         );
114:     }
115: 
116:     public function attributeLabels(){
117:         return array(
118:             'id' => 'ID',
119:             'assignedTo' => Yii::t('contacts', 'Owner'),
120:             'name' => Yii::t('contacts', 'Name'),
121:             'description' => Yii::t('contacts', 'Description'),
122:             'type' => Yii::t('contacts', 'Type'),
123:             'logicType' => Yii::t('contacts', 'Logic Type'),
124:             'modelName' => Yii::t('contacts', 'Record Type'),
125:             'visibility' => Yii::t('contacts', 'Visibility'),
126:             'count' => Yii::t('contacts', 'Members'),
127:             'createDate' => Yii::t('contacts', 'Create Date'),
128:             'lastUpdated' => Yii::t('contacts', 'Last Updated'),
129:         );
130:     }
131: 
132:     /**
133:      * An array of valid comparison operators for criteria in dynamic lists
134:      * @return array
135:      */
136:     public static function getComparisonList(){
137:         return array(
138:             '=' => Yii::t('contacts', 'equals'),
139:             '>' => Yii::t('contacts', 'greater than'),
140:             '<' => Yii::t('contacts', 'less than'),
141:             '<>' => Yii::t('contacts', 'not equal to'),
142:             'contains' => Yii::t('contacts', 'contains'),
143:             'noContains' => Yii::t('contacts', 'does not contain'),
144:             'empty' => Yii::t('contacts', 'empty'),
145:             'notEmpty' => Yii::t('contacts', 'not empty'),
146:             'list' => Yii::t('contacts', 'in list'),
147:             'notList' => Yii::t('contacts', 'not in list'),
148:         );
149:     }
150: 
151:     public function getDefaultRoute(){
152:         return '/contacts/contacts/list';
153:     }
154: 
155:     public function getDisplayName ($plural=true, $ofModule=true) {
156:         return Yii::t('contacts', '{contact} Lists|{contact} List', array(
157:             (int) $plural,
158:             '{contact}' => Modules::displayName(false, 'Contacts'),
159:         ));
160:     }
161: 
162:     public function createLink(){
163:         if(isset($this->id))
164:             return CHtml::link($this->name, array($this->getDefaultRoute(), 'id' => $this->id));
165:         else
166:             return $this->name;
167:     }
168: 
169:     /**
170:      * When a list is deleted, remove its entries in x2_list_items
171:      */
172:     public function afterDelete(){
173:         CActiveRecord::model('X2ListItem')->deleteAllByAttributes(array('listId' => $this->id)); // delete all the things!
174: 
175:         parent::afterDelete();
176:     }
177: 
178:     /**
179:      * Retrieves a list of models based on the current search/filter conditions.
180:      * @return CActiveDataProvider the data provider that can return the models based on the search/filter conditions.
181:      */
182:     public function search(){
183:         // Warning: Please modify the following code to remove attributes that
184:         // should not be searched.
185: 
186:         $criteria = new CDbCriteria;
187: 
188:         $criteria->compare('id', $this->id, true);
189:         $criteria->compare('name', $this->name, true);
190:         $criteria->compare('description', $this->description, true);
191:         $criteria->compare('type', $this->type, true);
192:         $criteria->compare('logicType', $this->logicType, true);
193:         $criteria->compare('modelName', $this->modelName, true);
194:         $criteria->compare('createDate', $this->createDate, true);
195:         $criteria->compare('lastUpdated', $this->lastUpdated, true);
196: 
197:         return new CActiveDataProvider(get_class($this), array(
198:                     'criteria' => $criteria,
199:                 ));
200:     }
201: 
202:     // get fields for listed model
203:     public function getItemFields(){
204:         if(empty($this->_itemFields)){
205:             if(!isset($this->_itemModel) && class_exists($this->modelName))
206:                 $this->_itemModel = new $this->modelName;
207:             $this->_itemFields = $this->_itemModel->fields;
208:         }
209:         return $this->_itemFields;
210:     }
211: 
212:     // get attribute labels for listed model
213:     public function getItemAttributeLabels(){
214:         if(empty($this->_itemAttributeLabels)){
215:             if(!isset($this->_itemModel) && class_exists($this->modelName))
216:                 $this->_itemModel = new $this->modelName;
217:             $this->_itemAttributeLabels = $this->_itemModel->attributeLabels();
218:         }
219:         return $this->_itemAttributeLabels;
220:     }
221: 
222:     public static function load($id){
223:         if(!Yii::app()->params->isAdmin){
224:             $condition = 't.visibility="1" OR t.assignedTo="Anyone"  OR t.assignedTo="'.Yii::app()->user->getName().'"';
225:             /* x2temp */
226:             $groupLinks = Yii::app()->db->createCommand()->select('groupId')->from('x2_group_to_user')->where('userId='.Yii::app()->user->getId())->queryColumn();
227:             if(!empty($groupLinks))
228:                 $condition .= ' OR t.assignedTo IN ('.implode(',', $groupLinks).')';
229: 
230:             $condition .= 'OR (t.visibility=2 AND t.assignedTo IN
231:                  (SELECT username FROM x2_group_to_user WHERE groupId IN
232:                      (SELECT groupId FROM x2_group_to_user WHERE userId='.Yii::app()->user->getId().')))';
233:         } else{
234:             $condition = '';
235:         }
236:         return self::model()->findByPk((int) $id, $condition);
237:     }
238: 
239:     public function calculateCount () {
240:         $criteria = $this->queryCriteria ();
241:         return Contacts::model ()->count ($criteria);
242:     }
243: 
244: 
245:     /**
246:      * Returns a CDbCriteria to retrieve all models specified by the list
247:      * @return CDbCriteria Criteria to retrieve all models in the list
248:      */
249:     public function queryCriteria($useAccessRules = true){
250:         $search = new CDbCriteria;
251: 
252:         if($this->type == 'dynamic'){
253:             $tagJoinCount = 0;
254:             $logicMode = $this->logicType;
255:             $criteria = X2Model::model('X2ListCriterion')
256:                 ->findAllByAttributes(array('listId' => $this->id, 'type' => 'attribute'));
257:             foreach($criteria as $criterion){
258:                 //if this criterion is for a date field, we perform its comparisons differently
259:                 $dateType = false;
260:                 //for each field in a model, make sure the criterion is in the same format
261:                 foreach(X2Model::model($this->modelName)->fields as $field){
262:                     if($field->fieldName == $criterion->attribute){
263:                         switch($field->type){
264:                             case 'date':
265:                             case 'dateTime':
266:                                 if(ctype_digit((string) $criterion->value) || 
267:                                    (substr($criterion->value, 0, 1) == '-' && 
268:                                     ctype_digit((string) substr($criterion->value, 1)))) {
269:                                     $criterion->value = (int) $criterion->value;
270:                                 } else {
271:                                     $criterion->value = strtotime($criterion->value);
272:                                 }
273:                                 $dateType = true;
274:                                 break;
275:                             case 'boolean':
276:                             case 'visibility':
277:                                 $criterion->value = in_array(
278:                                     strtolower($criterion->value), 
279:                                     array('1', 'yes', 'y', 't', 'true')) ? 1 : 0;
280:                                 break;
281:                         }
282:                         break;
283:                     }
284:                 }
285: 
286:                 if($criterion->attribute == 'tags' && $criterion->value){
287:                     //remove any spaces around commas, then explode to array
288:                     $tags = explode(',', preg_replace('/\s?,\s?/', ',', trim($criterion->value))); 
289:                     for($i = 0; $i < count($tags); $i++){
290:                         if(empty($tags[$i])){
291:                             unset($tags[$i]);
292:                             $i--;
293:                             continue;
294:                         }else{
295:                             if($tags[$i][0] != '#')
296:                                 $tags[$i] = '#'.$tags[$i];
297:                             $tags[$i] = 'x2_tags.tag = "'.$tags[$i].'"';
298:                         }
299:                     }
300:                     $tagCondition = implode(' OR ', $tags);
301:                     $search->join = 'LEFT JOIN x2_tags ON t.id = x2_tags.itemId';
302:                     $search->addCondition("x2_tags.id IS NOT NULL AND x2_tags.type='$this->modelName' AND " ."($tagCondition)", $logicMode);
303:                 } else if($dateType){
304:                     //assume for now that any dates in a criterion are at midnight of that day
305:                     $thisDay = $criterion->value;
306:                     $nextDay = $criterion->value + 86400;
307:                     switch($criterion->comparison){
308:                         case '=':
309:                             $subSearch = new CDbCriteria();
310:                             $subSearch->compare('t.'.$criterion->attribute, '>='.$thisDay, false, 'AND');
311:                             $subSearch->compare('t.'.$criterion->attribute, '<'.$nextDay, false, 'AND');
312:                             $search->mergeWith($subSearch, $logicMode);
313:                             break;
314:                         case '<>':
315:                             $subSearch = new CDbCriteria();
316:                             $subSearch->compare('t.'.$criterion->attribute, '<'.$thisDay, false, 'OR');
317:                             $subSearch->compare('t.'.$criterion->attribute, '>='.$nextDay, false, 'OR');
318:                             $search->mergeWith($subSearch, $logicMode);
319:                             break;
320:                         case '>':
321:                             $search->compare('t.'.$criterion->attribute, '>='.$thisDay, true, $logicMode);
322:                             break;
323:                         case '<':
324:                             $search->compare('t.'.$criterion->attribute, '<'.$thisDay, true, $logicMode);
325:                             break;
326:                         case 'notEmpty':
327:                             $search->addCondition('t.'.$criterion->attribute.' IS NOT NULL AND '.'t.'.$criterion->attribute.'!=""', $logicMode);
328:                             break;
329:                         case 'empty':
330:                             $search->addCondition('('.'t.'.$criterion->attribute.'="" OR '.'t.'.$criterion->attribute.' IS NULL)', $logicMode);
331:                             break;
332:                         //the following comparitors are not supported for dates
333:                         //case 'list':
334:                         //case 'notList':
335:                         //case 'noContains':
336:                         //case 'contains':
337:                     }
338:                 }else{
339:                     switch($criterion->comparison){
340:                         case '=':
341:                             $search->compare('t.'.$criterion->attribute, $criterion->value, false, $logicMode);
342:                             break;
343:                         case '>':
344:                             $search->compare('t.'.$criterion->attribute, '>='.$criterion->value, true, $logicMode);
345:                             break;
346:                         case '<':
347:                             $search->compare('t.'.$criterion->attribute, '<='.$criterion->value, true, $logicMode);
348:                             break;
349:                         case '<>': // must test for != OR is null, because both mysql and yii are stupid
350:                             $search->addCondition('('.'t.'.$criterion->attribute.' IS NULL OR '.'t.'.$criterion->attribute.'!='.CDbCriteria::PARAM_PREFIX.CDbCriteria::$paramCount.')', $logicMode);
351:                             $search->params[CDbCriteria::PARAM_PREFIX.CDbCriteria::$paramCount++] = $criterion->value;
352:                             break;
353:                         case 'notEmpty':
354:                             $search->addCondition('t.'.$criterion->attribute.' IS NOT NULL AND '.'t.'.$criterion->attribute.'!=""', $logicMode);
355:                             break;
356:                         case 'empty':
357:                             $search->addCondition('('.'t.'.$criterion->attribute.'="" OR '.'t.'.$criterion->attribute.' IS NULL)', $logicMode);
358:                             break;
359:                         case 'list':
360:                             $search->addInCondition('t.'.$criterion->attribute, explode(',', $criterion->value), $logicMode);
361:                             break;
362:                         case 'notList':
363:                             $search->addNotInCondition('t.'.$criterion->attribute, explode(',', $criterion->value), $logicMode);
364:                             break;
365:                         case 'noContains':
366:                             $search->compare('t.'.$criterion->attribute, '<>'.$criterion->value, true, $logicMode);
367:                             break;
368:                         case 'contains':
369:                         default:
370:                             $search->compare('t.'.$criterion->attribute, $criterion->value, true, $logicMode);
371:                     }
372:                 }
373:             }
374:         }else{
375:             $search->join = 'JOIN x2_list_items ON t.id = x2_list_items.contactId';
376:             $search->addCondition('x2_list_items.listId='.$this->id);
377:         }
378: 
379:         if($useAccessRules){
380:             $accessCriteria = X2Model::model('Contacts')->getAccessCriteria(); // record-level access control for Contacts
381:             $accessCriteria->mergeWith($search, 'AND');
382:             return $accessCriteria;
383:         }else{
384:             return $search;
385:         }
386:     }
387: 
388:     /**
389:      * Returns a CDbCommand to retrieve all records in the list
390:      * @return CDbCommand Command to retrieve all records in the list
391:      */
392:     public function queryCommand($useAccessRules = true){
393:         $tableSchema = X2Model::model($this->modelName)->getTableSchema();
394:         return $this->getCommandBuilder()->createFindCommand($tableSchema, $this->queryCriteria($useAccessRules));
395:     }
396: 
397:     /**
398:      * Returns a data provider for all the models in the list
399:      * @return CActiveDataProvider A data provider serving out all the models in the list
400:      */
401:     public function dataProvider($pageSize = null, $sort = null){
402:         if(!isset($sort))
403:             $sort = array();
404:         return new CActiveDataProvider($this->modelName, array(
405:                     'criteria' => $this->queryCriteria(),
406:                     'pagination' => array(
407:                         'pageSize' => isset($pageSize) ? $pageSize : Profile::getResultsPerPage(),
408:                     ),
409:                     'sort' => $sort
410:                 ));
411:     }
412: 
413:     /**
414:      * Generates an array of links for the VCR controls based on the specified dataprovider and 
415:      * current ID
416:      * @param CActiveDataProvider $dataProvider the data provider of the most recent gridview
417:      * @param Integer $id the ID of the current record
418:      * @return Array array of VCR links and stats
419:      */
420:     public static function getVcrLinks(&$dataProvider, $modelId){
421: 
422:         $criteria = $dataProvider->criteria;
423: 
424:         $tableSchema = X2Model::model($dataProvider->modelClass)->getTableSchema();
425:         if($tableSchema === null)
426:             return false;
427: 
428:         // for the first query, find the current ID's row number in the list
429:         $criteria->select = 't.id';
430: 
431:         // we also need any columns that are being used in the sort
432:         foreach(explode(',', $criteria->order) as $token){
433: 
434:             // so loop through $criteria->order and extract them
435:             $token = preg_replace('/\s|asc|desc/i', '', $token);
436:             if($token !== '' && $token !== 'id' && $token != 't.id'){
437:                 if(strpos($token, '.') != 1){
438:                     $criteria->select .= ',t.'.$token;
439:                 }else{
440:                     $criteria->select .= ','.$token;
441:                 }
442:             }
443:         }
444: 
445:         // always include "id DESC" in sorting (for order consistency with SmartDataProvider)
446:         if(!preg_match('/\bid\b/', $criteria->order)){
447:             if(!empty($criteria->order))
448:                 $criteria->order .= ',';
449:             $criteria->order .= 't.id DESC';
450:         }
451: 
452:         // get search conditions (WHERE, JOIN, ORDER BY, etc) from the criteria
453:         $searchConditions = Yii::app()->db->getCommandBuilder()
454:             ->createFindCommand($tableSchema, $criteria)->getText();
455:         
456:         
457:         /*
458:          * VCR Button Row Number Selection Query
459:          * 
460:          * This complicated block of code defines where a record is in the row
461:          * set to determine its position for VCR controls. This relies on SQL
462:          * variables and incrementing the variable in each row of the result set
463:          * from the subquery. A version of this query in plain MySQL looks like:
464:          * SELECT r-1 
465:          *   FROM (
466:          *       SELECT *,@rownum:=@rownum + 1 AS r 
467:          *       FROM ('.$searchConditions.') t1, (SELECT @rownum:=0) r) t2 
468:          *   WHERE t2.id='.$modelId
469:          */
470:         $varPrefix = '@'; //Current prefix is MySQL specific
471:         $varName = $varPrefix.'rownum';
472:         $varText = 'SET '.$varName.' = 0'; // Current declaration is MySQL specific
473:         Yii::app()->db->createCommand()
474:                 ->setText($varText)
475:                 ->execute();
476:         $subQuery = Yii::app()->db->createCommand()
477:                 ->select('*, ('.$varName.':='.$varName.'+1) r')
478:                 ->from('('.$searchConditions.') t1')
479:                 ->getText();
480:         $rowNumberQuery = Yii::app()->db->createCommand()
481:                 ->select('(r-1)')
482:                 ->from('('.$subQuery.') t2')
483:                 ->where('t2.id=:t2_id');
484:         
485: //        $rowNumberQuery = Yii::app()->db->createCommand('
486: //            SELECT r-1 
487: //            FROM (
488: //                SELECT *,@rownum:=@rownum + 1 AS r 
489: //                FROM ('.$searchConditions.') t1, (SELECT @rownum:=0) r) t2 
490: //            WHERE t2.id='.$modelId
491: //        );
492:         // attach params from $criteria to this query
493:         $rowNumberQuery->params = array_merge(array(':t2_id'=>$modelId),$criteria->params);
494:         $rowNumber = $rowNumberQuery->queryScalar();
495: 
496:         if($rowNumber === false){ // the specified record isn't in this list
497:             return false;
498:         }else{
499: 
500:             $criteria->select = '*'; // need to select everything to be sure ORDER BY will work
501: 
502:             if($rowNumber == 0){ // if we're on the first row, get 2 items, otherwise get 3
503:                 $criteria->offset = 0;
504:                 $criteria->limit = 2;
505:                 $vcrIndex = 0;
506:             }else{
507:                 $criteria->offset = $rowNumber - 1;
508:                 $criteria->limit = 3;
509:                 $vcrIndex = 1;  // index of current record in $vcrModels
510:             }
511: 
512:             $vcrModels = Yii::app()->db->getCommandBuilder()
513:                             ->createFindCommand($tableSchema, $criteria)->queryAll();
514:             $count = $dataProvider->getTotalItemCount();
515: 
516:             $vcrData = array();
517:             $vcrData['index'] = $rowNumber + 1;
518:             $vcrData['count'] = $dataProvider->getTotalItemCount();
519: 
520:             /* if($vcrIndex > 0)        // there's a record before the current one
521:               $vcrData['prev'] = '<li class="prev">'.CHtml::link('<',array('view/'.$vcrModels[0]['id']),array('title'=>$vcrModels[0]['name'],'class'=>'x2-button')).'</li>';
522:               else
523:               $vcrData['prev'] = '<li class="prev">'.CHtml::link('<','javascript:void(0);',array('class'=>'x2-button disabled')).'</li>';
524: 
525:               if(count($vcrModels) - 1 > $vcrIndex) // there's a record after the current one
526:               $vcrData['next'] = '<li class="next">'.CHtml::link('>', array('view/'.$vcrModels[$vcrIndex+1]['id']), array('title'=>$vcrModels[$vcrIndex+1]['name'],'class'=>'x2-button')).'</li>';
527:               else
528:               $vcrData['next'] = '<li class="next">'.CHtml::link('>','javascript:void(0);',array('class'=>'x2-button disabled')).'</li>';
529:              */
530:             if($vcrIndex > 0 && isset($vcrModels[0])){ // there's a record before the current one
531:                 $vcrData['prev'] = CHtml::link(
532:                     '<', array('view', 'id' => $vcrModels[0]['id']), 
533:                     array('title' => $vcrModels[0]['name'], 'class' => 'x2-button'));
534:             }else{
535:                 $vcrData['prev'] = CHtml::link(
536:                     '<', 'javascript:void(0);', array('class' => 'x2-button disabled'));
537:             }
538: 
539:             if(count($vcrModels) - 1 > $vcrIndex){ // there's a record after the current one
540:                 $vcrData['next'] = CHtml::link(
541:                     '>', array('view', 'id' => $vcrModels[$vcrIndex + 1]['id']), 
542:                     array('title' => $vcrModels[$vcrIndex + 1]['name'], 'class' => 'x2-button'));
543:             }else{
544:                 $vcrData['next'] = CHtml::link(
545:                     '>', 'javascript:void(0);', array('class' => 'x2-button disabled'));
546:             }
547: 
548:             return $vcrData;
549:         }
550:     }
551: 
552:     /**
553:      * Returns an array data provider for all models in the list,
554:      * including the list_item status columns
555:      * @return CSqlDataProvider
556:      */
557:     public function statusDataProvider($pageSize = null){
558:         $tbl = X2Model::model($this->modelName)->tableName();
559:         $lstTbl = X2ListItem::model()->tableName();
560:         if($this->type == 'dynamic'){
561:             $criteria = $this->queryCriteria();
562:             $count = X2Model::model($this->modelName)->count($criteria);
563:             $sql = $this->getCommandBuilder()->createFindCommand($tbl, $criteria)->getText();
564:             $params = $criteria->params;
565:         }else{ //static type lists
566:             $count = X2ListItem::model()->count('listId=:listId', array('listId' => $this->id));
567:             $sql = "SELECT t.*, c.* FROM {$lstTbl} as t LEFT JOIN {$tbl} as c ON t.contactId=c.id WHERE t.listId=:listId;";
568:             $params = array('listId' => $this->id);
569:         }
570:         return new CSqlDataProvider($sql, array(
571:                     'totalItemCount' => $count,
572:                     'params' => $params,
573:                     'pagination' => array(
574:                         'pageSize' => isset($pageSize) ? $pageSize : Profile::getResultsPerPage(),
575:                     ),
576:                     'sort' => array(
577:                         //messing with attributes may cause columns to become unsortable
578:                         'attributes' => array('name', 'email', 'phone', 'address'),
579:                         'defaultOrder' => 'lastUpdated DESC',
580:                     ),
581:                 ));
582:     }
583: 
584:     /**
585:      * Return a SQL data provider for a list of emails in a campaign
586:      * includes associated contact info with each email
587:      * @return CSqlDataProvider
588:      */
589:     public function campaignDataProvider($pageSize = null){
590:         $criteria = X2Model::model('Campaign')->getAccessCriteria();
591:         $conditions =$criteria->condition;
592: 
593:         $params = array('listId' => $this->id);
594: 
595:         $count = Yii::app()->db->createCommand()
596:                 ->select('count(*)')
597:                 ->from(X2ListItem::model()->tableName().' as list')
598:                 ->leftJoin(X2Model::model($this->modelName)->tableName().' t', 'list.contactId=t.id')
599:                 ->where(
600:                     'list.listId=:listId AND ('.$conditions.')',
601:                     array_merge (array(
602:                         ':listId' => $this->id
603:                     ), $criteria->params))
604:                 ->queryScalar();
605: 
606:         $sql = Yii::app()->db->createCommand()
607:                 ->select('list.*, t.*')
608:                 ->from(X2ListItem::model()->tableName().' as list')
609:                 ->leftJoin(X2Model::model($this->modelName)->tableName().' t', 'list.contactId=t.id')
610:                 ->where(
611:                     'list.listId=:listId AND ('.$conditions.')')
612:                 ->getText();
613: 
614:         return new CSqlDataProvider($sql, array(
615:                     'params' => array_merge ($params, $criteria->params),
616:                     'totalItemCount' => $count,
617:                     'pagination' => array(
618:                         'pageSize' => !empty($pageSize) ? $pageSize : Profile::getResultsPerPage(),
619:                     ),
620:                     'sort' => array(
621:                         //messing with attributes may cause columns to become unsortable
622:                         'attributes' => array(
623:                             'name', 
624:                             'email', 
625:                             'phone', 
626:                             'address', 
627:                             'sent',
628:                             'opened',
629:                             'clicked',
630:                             'unsubscribed',
631:                             'doNotEmail',
632:                         ),
633:                         'defaultOrder' => 'opened DESC, sent DESC, name DESC',
634:                     ),
635:                 ));
636:     }
637: 
638:     /**
639:      * Returns the count of items in the list that have the specified status
640:      * (i.e. sent, opened, clicked, unsubscribed)
641:      * @return integer
642:      */
643:     public function statusCount($type){
644:         $whitelist = array('sent', 'opened', 'clicked', 'unsubscribed');
645:         if(!in_array($type, $whitelist)){
646:             return 0;
647:         }
648: 
649:         $lstTbl = X2ListItem::model()->tableName();
650:         $count = Yii::app()->db->createCommand(
651:                         'SELECT COUNT(*) FROM '.$lstTbl.' WHERE listId = :listid AND '.$type.' > 0')
652:                 ->queryScalar(array('listid' => $this->id));
653:         return $count;
654:     }
655: 
656:     /**
657:      * Creates, saves, and returns a duplicate static list containing the same items.
658:      *
659:      * @return X2List|null The active record model for the static clone is returned
660:      *  if the operation was successful; otherwise Null is returned.
661:      */
662:     public function staticDuplicate(){
663:         $dup = new X2List();
664:         $dup->attributes = $this->attributes;
665:         $dup->id = null;
666:         $dup->nameId = null;
667:         $dup->type = 'static';
668:         $dup->createDate = $dup->lastUpdated = time();
669:         $dup->isNewRecord = true;
670:         if(!$dup->save())
671:             return;
672: 
673:         $count = 0;
674:         $listItemRecords = array();
675:         $params = array();
676:         if($this->type == 'dynamic'){
677:             $itemIds = $this->queryCommand(true)->select('id')->queryColumn();
678:             foreach($itemIds as $id){
679:                 $listItemRecords[] = '(NULL,:contactId'.$count.',:listId'.$count.',0)';
680:                 $params[':contactId'.$count] = $id;
681:                 $params[':listId'.$count] = $dup->id;
682:                 $count++;
683:             }
684:         }else{ //static type lists
685:             //generate sql to replicate list items
686:             foreach($this->listItems as $listItem){
687:                 if(!empty($listItem->emailAddress)){
688:                     $itemSql = '(:email'.$count;
689:                     $params[':email'.$count] = $listItem->emailAddress;
690:                 }else{
691:                     $itemSql = '(NULL';
692:                 }
693:                 if(!empty($listItem->contactId)){
694:                     $itemSql .= ',:contactId'.$count;
695:                     $params[':contactId'.$count] = $listItem->contactId;
696:                 }else{
697:                     $itemSql .= ',NULL';
698:                 }
699:                 $itemSql .= ',:listId'.$count.',:unsubd'.$count.')';
700:                 $params[':listId'.$count] = $dup->id;
701:                 $params[':unsubd'.$count] = $listItem->unsubscribed;
702:                 $listItemRecords[] = $itemSql;
703:                 $count++;
704:             }
705:         }
706:         if(count($listItemRecords) == 0)
707:             return;
708:         $sql = 'INSERT into x2_list_items
709:             (emailAddress, contactId, listId, unsubscribed)
710:             VALUES '
711:                 .implode(',', $listItemRecords).';';
712:         $dup->count = $count;
713: 
714:         $transaction = Yii::app()->db->beginTransaction();
715:         try{
716:             Yii::app()->db->createCommand($sql)->execute($params);
717:             $transaction->commit();
718:         }catch(Exception $e){
719:             $transaction->rollBack();
720:             $dup->delete();
721:             Yii::log($e->getMessage(), 'error', 'application');
722:             $dup = null;
723:         }
724:         return $dup;
725:     }
726: 
727:     /**
728:      * Adds the specified ID(s) to this list (if they're not already in there)
729:      * @param mixed $ids a single integer or an array of integer IDs
730:      */
731:     public function addIds($ids){
732:         if($this->type !== 'static')
733:             return false;
734: 
735:         $ids = (array) $ids;
736: 
737:         $parameters = AuxLib::bindArray($ids, 'addIds');
738:         $existingIds = Yii::app()->db->createCommand()
739:                 ->select('contactId')
740:                 ->from('x2_list_items')
741:                 ->where('listId='.$this->id.' AND contactId IN('.implode(',', array_keys($parameters)).')', $parameters) // intersection of $ids and the IDs already in this list
742:                 ->queryColumn();
743: 
744:         foreach($ids as $id){
745:             if(in_array($id, $existingIds))
746:                 continue;
747:             $listItem = new X2ListItem();
748:             $listItem->listId = $this->id;
749:             $listItem->contactId = $id;
750:             $listItem->save();
751:         }
752: 
753:         $this->count = CActiveRecord::model('X2ListItem')->countByAttributes(array('listId' => $this->id));
754:         return $this->update(array('count'));
755:     }
756: 
757:     /**
758:      * Save associated criterion objects for a dynamic list
759:      *
760:      * Takes data from the dynamic list criteria designer form and turns them
761:      * into {@link X2ListCriterion} records.
762:      */
763:     public function processCriteria(){
764:         X2ListCriterion::model()->deleteAllByAttributes(array('listId' => $this->id)); // delete old criteria
765:         foreach(array('attribute', 'comparison', 'value') as $property){
766:             // My lazy refactor: bring properties into the current scope as
767:             // temporary variables with their names pluralized
768:             ${"{$property}s"} = $this->criteriaInput[$property];
769:         }
770:         $comparisonList = self::getComparisonList();
771:         $contactModel = Contacts::model();
772:         $fields = $contactModel->getFields(true);
773: 
774:         for($i = 0; $i < count($attributes); $i++){ // create new criteria
775:             if((array_key_exists($attributes[$i], $contactModel->attributeLabels()) || $attributes[$i] == 'tags') && array_key_exists($comparisons[$i], $comparisonList)){
776:                 $fieldRef = isset($fields[$attributes[$i]]) ? $fields[$attributes[$i]] : null;
777:                 if($fieldRef instanceof Fields && $fieldRef->type == 'link'){
778:                     $nameList = explode(',', $values[$i]);
779:                     $namesParams = AuxLib::bindArray($nameList);
780:                     $namesIn = AuxLib::arrToStrList(array_keys($namesParams));
781:                     $lookupModel = X2Model::model(ucfirst($fieldRef->linkType));
782:                     $lookupModels = $lookupModel->findAllBySql(
783:                             'SELECT * FROM `'.$lookupModel->tableName().'` '
784:                             .'WHERE `name` IN '.$namesIn, $namesParams);
785:                     if(!empty($lookupModels)){
786:                         $values[$i] = implode(',', array_map(function($m){
787:                                     return $m->nameId;
788:                                 }, $lookupModels)); //$lookup->nameId;
789:                     }
790:                 }
791:                 $criterion = new X2ListCriterion;
792:                 $criterion->listId = $this->id;
793:                 $criterion->type = 'attribute';
794:                 $criterion->attribute = $attributes[$i];
795:                 $criterion->comparison = $comparisons[$i];
796:                 $criterion->value = $values[$i];
797:                 $criterion->save();
798:             }
799:         }
800:     }
801: 
802:     /**
803:      * Removes the specified ID(s) from this list
804:      * @param mixed $ids a single integer or an array of integer IDs
805:      */
806:     public function removeIds($ids){
807:         if($this->type !== 'static')
808:             return false;
809: 
810:         $criteria = new CDbCriteria();
811:         $criteria->compare('listId', $this->id);
812:         $criteria->addInCondition('contactId', (array) $ids);
813: 
814:         $model = CActiveRecord::model('X2ListItem');
815: 
816:         // delete all the things!
817:         if(CActiveRecord::model('X2ListItem')->deleteAll($criteria)){
818:             $this->count = CActiveRecord::model('X2ListItem')->countByAttributes(array('listId' => $this->id));
819:             $this->update(array('count'));
820:             return true;
821:         }
822:         return false;
823:     }
824: 
825:     /**
826:      * Uses {@link queryCriteria()} to test whether this list contains the specified record ID
827:      * @param integer $id the ID of the record
828:      * @return boolean whether or not record is in this list
829:      */
830:     /*public function hasRecord($id){
831:         $criteria = $this->queryCriteria(false); // don't use access rules
832:         $criteria->compare('id', $id);
833: 
834:         return X2Model::model($this->modelName)->exists($criteria);
835:     }*/
836: 
837:     public function hasRecord ($model) {
838:         if($this->modelName !== get_class($model))
839:             return false;
840:         $listCriteria = $this->queryCriteria(false); // don't use access rules
841:         $listCriteria->compare('t.id',$model->id);
842:         return $model->exists($listCriteria);        // see if this record is on the list
843:     }
844: 
845:     public static function getRoute($id){
846:         if($id == 'all')
847:             return array('/contacts/contacts/index');
848:         else if($id == 'new')
849:             return array('/contacts/contacts/newContacts');
850:         else if(empty($id) || $id == 'my')
851:             return array('/contacts/contacts/myContacts');
852:         else
853:             return array('/contacts/contacts/list', 'id' => $id);
854:     }
855: 
856:     public static function getAllStaticListNames($controller){
857:         $listNames = array();
858: 
859:         // get all static lists
860:         foreach(X2List::model()->findAllByAttributes(array('type' => 'static')) as $list){
861:             if($controller->checkPermissions($list, 'edit')) // check permissions
862:                 $listNames[$list->id] = $list->name;
863:         }
864:         return $listNames;
865:     }
866: 
867:     public function setAttributes($values, $safeOnly = true){
868:         if($this->type == 'dynamic'){
869:             $this->criteriaInput = array();
870:             foreach(array('attribute', 'comparison', 'value') as $property){
871:                 if(isset($values[$property]))
872:                     $this->criteriaInput[$property] = $values[$property];
873:             }
874:             $criteria = array_combine ($this->criteriaInput['attribute'], $this->criteriaInput['value']);
875:             if (array_key_exists ('tags', $criteria) && empty($criteria['tags'])) {
876:                 $this->addError ('tags', Yii::t ('contacts', 'Tag list must be non-empty'));
877:             }
878:         }
879:         parent::setAttributes($values, $safeOnly);
880:     }
881: 
882:     public function afterSave(){
883:         if($this->type == 'dynamic' && isset($this->criteriaInput)) {
884:             $this->processCriteria();
885:         }
886:         return parent::afterSave();
887:     }
888: 
889:     /**
890:      * Filter array of lists based on attributes of this list
891:      */
892:     public function filter (array $lists) {
893:         $filterAttrs = array ();
894:         foreach ($this->getAttributes () as $attr => $val) {
895:             if (isset ($val) && $val !== '') {
896:                 if ($attr === 'assignedTo') {
897:                     $filterAttrs[$attr] = $this->compareAssignment ($val);
898:                     if (is_array ($filterAttrs[$attr])) {
899:                         $filterAttrs[$attr] =  array_map (function ($elem) {
900:                             return strtolower ($elem); 
901:                         }, $filterAttrs[$attr]);
902:                     }
903:                 } else {
904:                     $filterAttrs[$attr] = $val;
905:                 }
906:             }
907:         }
908: 
909:         $filteredLists = array ();
910:         foreach ($lists as $list) {
911:             $pass = true;
912:             foreach ($filterAttrs as $attr => $val) {
913:                 if ($attr === 'assignedTo') {
914:                     if (!is_array ($val) ||
915:                         !in_array (strtolower ($list->$attr), $val)) {
916: 
917:                         $pass = false;
918:                         break;
919:                     }
920:                 } elseif (!preg_match ("/$val/i", (string) $list->$attr)) {
921:                     $pass = false;
922:                     break;
923:                 }
924:             }
925:             if ($pass) $filteredLists[] = $list;
926:         }
927:         return $filteredLists;
928:     }
929: 
930: }
931: 
X2CRM Documentation API documentation generated by ApiGen 2.8.0