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

  • AdminController
  • Api2Controller
  • ApiController
  • BugReportsController
  • CommonSiteControllerBehavior
  • ProfileController
  • RelationshipsController
  • SearchController
  • SiteController
  • StudioController
  • TemplatesController
  • TopicsController
  • x2base
  • X2Controller
  • Overview
  • Package
  • Class
  • Tree
  1: <?php
  2: 
  3: /*****************************************************************************************
  4:  * X2Engine Open Source Edition is a customer relationship management program developed by
  5:  * X2Engine, Inc. Copyright (C) 2011-2016 X2Engine Inc.
  6:  * 
  7:  * This program is free software; you can redistribute it and/or modify it under
  8:  * the terms of the GNU Affero General Public License version 3 as published by the
  9:  * Free Software Foundation with the addition of the following permission added
 10:  * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
 11:  * IN WHICH THE COPYRIGHT IS OWNED BY X2ENGINE, X2ENGINE DISCLAIMS THE WARRANTY
 12:  * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
 13:  * 
 14:  * This program is distributed in the hope that it will be useful, but WITHOUT
 15:  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 16:  * FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
 17:  * details.
 18:  * 
 19:  * You should have received a copy of the GNU Affero General Public License along with
 20:  * this program; if not, see http://www.gnu.org/licenses or write to the Free
 21:  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 22:  * 02110-1301 USA.
 23:  * 
 24:  * You can contact X2Engine, Inc. P.O. Box 66752, Scotts Valley,
 25:  * California 95067, USA. or at email address contact@x2engine.com.
 26:  * 
 27:  * The interactive user interfaces in modified source and object code versions
 28:  * of this program must display Appropriate Legal Notices, as required under
 29:  * Section 5 of the GNU Affero General Public License version 3.
 30:  * 
 31:  * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
 32:  * these Appropriate Legal Notices must retain the display of the "Powered by
 33:  * X2Engine" logo. If the display of the logo is not reasonably feasible for
 34:  * technical reasons, the Appropriate Legal Notices must display the words
 35:  * "Powered by X2Engine".
 36:  *****************************************************************************************/
 37: 
 38: /**
 39:  * Global internal searching through records.
 40:  *
 41:  * @package application.controllers
 42:  */
 43: class SearchController extends x2base {
 44: 
 45:     /**
 46:      * Specifies the access control rules.
 47:      * This method is used by the 'accessControl' filter.
 48:      * @return array access control rules
 49:      */
 50:     public function accessRules(){
 51:         return array(
 52:             array('allow', // allow authenticated user to perform 'create' and 'update' actions
 53:                 'actions' => array('search', 'buildIndex'),
 54:                 'users' => array('@'),
 55:             ),
 56:             array('deny', // deny all users
 57:                 'users' => array('*'),
 58:             ),
 59:         );
 60:     }
 61: 
 62:     public function filters(){
 63:         return array(
 64:             'setPortlets',
 65:             'accessControl',
 66:         );
 67:     }
 68: 
 69:     /**
 70:      * Rebuilds the search index.
 71:      */
 72:     public function actionBuildIndex(){
 73: 
 74:         $contact = new Contacts;
 75:         $fieldData = $contact->getFields();
 76: 
 77: 
 78:         // lookup searchable fields
 79:         $fields = array();
 80:         for($i = 0; $i < count($fieldData); $i++){
 81:             if(in_array($fieldData[$i]->type, array('dropdown', 'text', 'varchar', 'assignment'))
 82:                     && !in_array($fieldData[$i]->fieldName, array('firstName', 'lastName', 'updatedBy', 'priority', 'id'))
 83:             ){ //,'phone','url','email','link',
 84:                 // || !$fields[$i]->searchable
 85:                 if($fieldData[$i]->relevance == 'High')
 86:                     $relevance = 3;
 87:                 elseif($fieldData[$i]->relevance == 'Medium')
 88:                     $relevance = 2;
 89:                 else
 90:                     $relevance = 1;
 91: 
 92:                 $fields[$fieldData[$i]->fieldName] = array(
 93:                     'type' => $fieldData[$i]->type,
 94:                     'linkType' => $fieldData[$i]->linkType,
 95:                     'relevance' => $relevance
 96:                 );
 97:             }
 98:         }
 99: 
100:         $t0 = microtime(true);
101: 
102: 
103:         $totalCount = Yii::app()->db->createCommand('SELECT count(*) from x2_contacts;')->queryScalar();
104: 
105:         $dataProvider = new CSqlDataProvider('SELECT '.implode(',', array_merge(array_keys($fields), array('id', 'visibility'))).' FROM x2_contacts', array(
106:                     // 'criteria'=>array(
107:                     // 'order'=>'id ASC',
108:                     // ),
109:                     'totalItemCount' => $totalCount,
110:                     'sort' => array('defaultOrder' => 'id ASC'),
111:                     'pagination' => array(
112:                         'pageSize' => 500,
113:                     ),
114:                 ));
115:         $dataProvider->getData();
116: 
117: 
118:         $pages = $dataProvider->pagination->getPageCount();
119:         echo $pages.' pages.<br>';
120:         $searchTerms = array();
121: 
122:         // $fh = fopen('search.csv','w+');
123: 
124:         ob_end_flush();
125: 
126:         $keys = array();
127:         $tokenChars = " \n\r\t!$%^&*()_+-=~[]{}\\|:;'\",.<>?/`‘’•–—“”";
128:         $noiseWords = array(
129:             'a', 'about', 'after', 'all', 'also', 'an', 'and', 'another', 'any', 'are', 'arent', 'as', 'at', 'back', 'be', 'because', 'been', 'before',
130:             'being', 'between', 'both', 'but', 'by', 'came', 'can', 'cant', 'come', 'contact', 'contacts', 'contacted', 'could', 'data', 'did', 'didnt',
131:             'do', 'dont', 'does', 'doesnt', 'each', 'for', 'from', 'get', 'go', 'going', 'goes', 'got', 'has', 'hasnt', 'had', 'hadnt', 'he', 'hes', 'his',
132:             'hed', 'have', 'havent', 'her', 'hers', 'here', 'heres', 'him', 'himself', 'how', 'i', 'if', 'in', 'into', 'is', 'it', 'its', 'like', 'make',
133:             'made', 'makes', 'many', 'me', 'might', 'mightnt', 'more', 'most', 'much', 'must', 'mustnt', 'my', 'mine', 'never', 'no', 'now', 'not', 'of',
134:             'on', 'only', 'onto', 'or', 'other', 'our', 'out', 'over', 'said', 'same', 'see', 'she', 'shes', 'should', 'shouldnt', 'since', 'some', 'still',
135:             'such', 'take', 'than', 'that', 'the', 'their', 'them', 'then', 'there', 'theres', 'these', 'they', 'theyre', 'this', 'those', 'through', 'to',
136:             'too', 'today', 'under', 'up', 'very', 'want', 'wants', 'wanted', 'was', 'wasnt', 'way', 'ways', 'we', 'well', 'were', 'what', 'whats', 'where',
137:             'which', 'while', 'who', 'why', 'will', 'with', 'would', 'wont', 'you', 'your', 'youre'
138:         );
139: 
140: 
141:         for($i = 1; $i <= $pages; ++$i){
142:             // for($i = 1; $i<=1; ++$i) {
143: 
144:             $links = array();
145: 
146:             $dataProvider->pagination->setCurrentPage($i);
147: 
148:             foreach($dataProvider->getData($i > 1) as $record){
149:                 // var_dump($record);
150:                 foreach($fields as $fieldName => &$field){
151:                     // $fieldName = $field['fieldName'];
152: 
153:                     if(!empty($record[$fieldName])){
154:                         // break string into words, and eliminate any contractions so we can safely tokenize on ' characters
155:                         $token = strtok(preg_replace('/(?<=\w)\'(?=\w)/u', '', $record[$fieldName]), $tokenChars);
156:                         while($token !== false){
157:                             $token = strtolower($token);
158: 
159:                             if(strlen($token) <= 50 && !in_array($token, $noiseWords)){
160:                                 $links[] = array(
161:                                     $token,
162:                                     'Contacts',
163:                                     $record['id'],
164:                                     $field['relevance'],
165:                                     $record['assignedTo'],
166:                                     $record['visibility']
167:                                 );
168:                             }
169:                             $token = strtok($tokenChars);
170:                         }
171:                     }
172:                 }
173:                 unset($field);
174:             }
175: 
176:             $sql = 'INSERT INTO x2_search (keyword, modelType, modelId, relevance, assignedTo, visibility) VALUES ';
177:             for($j = 0; $j < count($links); ++$j){
178:                 $sql .= '(?,?,?,?,?,?)';
179:                 if($j < count($links) - 1)
180:                     $sql .= ',';
181:             }
182: 
183:             // echo $sql;
184:             // var_dump($links);
185:             // die();
186: 
187:             $query = Yii::app()->db->createCommand($sql);
188:             for($j = 0; $j < count($links); ++$j){
189:                 $query = $query->bindValues(array(
190:                     6 * $j + 1 => $links[$j][0],
191:                     6 * $j + 2 => $links[$j][1],
192:                     6 * $j + 3 => $links[$j][2],
193:                     6 * $j + 4 => $links[$j][3],
194:                     6 * $j + 5 => $links[$j][4],
195:                     6 * $j + 6 => $links[$j][5]
196:                         ));
197:             }
198:             // die(var_dump($links));
199:             // echo $query->getText();
200:             $query->execute();
201: 
202:             // break;
203:             echo "Page $i...done<br>";
204:             flush();
205:         }
206: 
207:         // Yii::app()->db->createCommand();
208: 
209:         echo 'Time: '.(microtime(true) - $t0).'<br>';
210:     }
211: 
212:     public function actionFastSearch(){
213: 
214:     }
215: 
216:     /**
217:      * Search X2Engine for a record.
218:      *
219:      * This is the action called by the search bar in the main menu.
220:      */
221:     public function actionSearch(){
222:         ini_set('memory_limit', -1);
223: 
224:         $term = isset($_GET['term']) ? $_GET['term'] : "";
225:         if (empty($term)) {
226:             $dataProvider = new CArrayDataProvider(array());
227:             Yii::app()->user->setFlash ('error', Yii::t('app', "Search term cannot be empty."));
228:             $this->render('search', array(
229:                 'dataProvider' => $dataProvider,
230:             ));
231:         } else {
232: 
233:             if(substr($term, 0, 1) != "#"){
234:     
235:                 $modules = Modules::model()->findAllByAttributes(array('searchable' => 1));
236:                 $comparisons = array();
237:                 $other = array();
238:                 foreach($modules as $module){
239:                     $module->name == 'products' ? $type = ucfirst('Product') : $type = ucfirst($module->name);
240:                     $module->name == 'quotes' ? $type = ucfirst('Quote') : $type = $type;
241:                     $module->name == 'opportunities' ? $type = ucfirst('Opportunity') : $type = $type;
242:                     $criteria = new CDbCriteria();
243:                     $fields = Fields::model()->findAllByAttributes(array('modelName' => $type, 'searchable' => 1));
244:                     $temp = array();
245:                     $fieldNames = array();
246:                     if(count($fields) < 1){
247:                         $criteria->compare('id', '<0', true, 'AND');
248:                     }
249:                     foreach($fields as $field){
250:                         $temp[] = $field->id;
251:                         $fieldNames[] = $field->fieldName;
252:                         $criteria->compare($field->fieldName, $term, true, "OR");
253:                         if($field->type == 'phone'){
254:                             $tempPhone = preg_replace('/\D/', '', $term);
255:                             $phoneLookup = PhoneNumber::model()->findByAttributes(array('modelType' => $field->modelName, 'number' => $tempPhone, 'fieldName' => $field->fieldName));
256:                             if(isset($phoneLookup)){
257:                                 $criteria->compare('id', $phoneLookup->modelId, true, "OR");
258:                             }
259:                         }
260:                     }
261:                     if(Yii::app()->user->getName() != 'admin' && X2Model::model($type)->hasAttribute('visibility') && X2Model::model($type)->hasAttribute('assignedTo')){
262:                         $condition = 'visibility="1" OR (assignedTo="Anyone" AND visibility!="0")  OR assignedTo="'.Yii::app()->user->getName().'"';
263:                         /* x2temp */
264:                         $groupLinks = Yii::app()->db->createCommand()->select('groupId')->from('x2_group_to_user')->where('userId='.Yii::app()->user->getId())->queryColumn();
265:                         if(!empty($groupLinks))
266:                             $condition .= ' OR assignedTo IN ('.implode(',', $groupLinks).')';
267:     
268:                         $condition .= 'OR (visibility=2 AND assignedTo IN
269:                             (SELECT username FROM x2_group_to_user WHERE groupId IN
270:                                 (SELECT groupId FROM x2_group_to_user WHERE userId='.Yii::app()->user->getId().')))';
271:                         $criteria->addCondition($condition);
272:                     }
273:                     if($module->name == 'actions'){
274:                         $criteria->with = array('actionText');
275:                         $criteria->compare('actionText.text', $term, true, "OR");
276:                     }
277:                     if(class_exists($type)){
278:                         $arr = X2Model::model($type)->findAll($criteria);
279:                         $comparisons[$type] = $temp;
280:                         $other[$type] = $arr;
281:                     }
282:                 }
283:                 $high = array();
284:                 $medium = array();
285:                 $low = array();
286:     
287:                 $userHigh = array();
288:                 $userMedium = array();
289:                 $userLow = array();
290:     
291:                 $records = array();
292:                 $userRecords = array();
293:     
294:                 $regEx = "/".preg_quote($term, '/')."/i";
295:     
296:                 foreach($other as $key => $recordType){
297:                     $fieldList = $comparisons[$key];
298:                     foreach($recordType as $otherRecord){
299:                         if($key == 'Actions'){
300:                             if($otherRecord->hasAttribute('assignedTo') && $otherRecord->assignedTo == Yii::app()->user->getName())
301:                                 $userHigh[] = $otherRecord;
302:                             else
303:                                 $high[] = $otherRecord;
304:                         }else{
305:                             foreach($fieldList as $field){
306:                                 $fieldRecord = Fields::model()->findByPk($field);
307:                                 $fieldName = $fieldRecord->fieldName;
308:                                 if(preg_match($regEx, $otherRecord->$fieldName) > 0){
309:                                     switch($fieldRecord->relevance){
310:                                         case "High":
311:                                             if(!in_array($otherRecord, $high, true) && !in_array($otherRecord, $medium, true) && !in_array($otherRecord, $low, true) &&
312:                                                     !in_array($otherRecord, $userHigh, true) && !in_array($otherRecord, $userMedium, true) && !in_array($otherRecord, $userLow, true)){
313:                                                 if($otherRecord->hasAttribute('assignedTo') && $otherRecord->assignedTo == Yii::app()->user->getName())
314:                                                     $userHigh[] = $otherRecord;
315:                                                 else
316:                                                     $high[] = $otherRecord;
317:                                             }
318:                                             break;
319:                                         case "Medium":
320:                                             if(!in_array($otherRecord, $high, true) && !in_array($otherRecord, $medium, true) && !in_array($otherRecord, $low, true) &&
321:                                                     !in_array($otherRecord, $userHigh, true) && !in_array($otherRecord, $userMedium, true) && !in_array($otherRecord, $userLow, true)){
322:                                                 if($otherRecord->hasAttribute('assignedTo') && $otherRecord->assignedTo == Yii::app()->user->getName())
323:                                                     $userMedium[] = $otherRecord;
324:                                                 else
325:                                                     $medium[] = $otherRecord;
326:                                             }
327:                                             break;
328:                                         case "Low":
329:                                             if(!in_array($otherRecord, $high, true) && !in_array($otherRecord, $medium, true) && !in_array($otherRecord, $low, true) &&
330:                                                     !in_array($otherRecord, $userHigh, true) && !in_array($otherRecord, $userMedium, true) && !in_array($otherRecord, $userLow, true)){
331:                                                 if($otherRecord->hasAttribute('assignedTo') && $otherRecord->assignedTo == Yii::app()->user->getName())
332:                                                     $userLow[] = $otherRecord;
333:                                                 else
334:                                                     $low[] = $otherRecord;
335:                                             }
336:                                             break;
337:                                         default:
338:                                             if($otherRecord->hasAttribute('assignedTo') && $otherRecord->assignedTo == Yii::app()->user->getName())
339:                                                 $userLow[] = $otherRecord;
340:                                             else
341:                                                 $low[] = $otherRecord;
342:                                     }
343:                                 }elseif($fieldRecord->type == 'phone'){
344:                                     $tempPhone = preg_replace('/\D/', '', $term);
345:     
346:                                     if(strlen($tempPhone) == 10){
347:                                         $phoneLookup = PhoneNumber::model()->findByAttributes(array('modelType' => $fieldRecord->modelName, 'number' => $tempPhone, 'fieldName' => $fieldName));
348:                                         if(!in_array($otherRecord, $high, true) && !in_array($otherRecord, $medium, true) && !in_array($otherRecord, $low, true) &&
349:                                                 !in_array($otherRecord, $userHigh, true) && !in_array($otherRecord, $userMedium, true) && !in_array($otherRecord, $userLow, true)){
350:                                             if(isset($phoneLookup) && $otherRecord->id == $phoneLookup->modelId){
351:                                                 if($otherRecord->hasAttribute('assignedTo') && $otherRecord->assignedTo == Yii::app()->user->getName())
352:                                                     $userHigh[] = $otherRecord;
353:                                                 else
354:                                                     $high[] = $otherRecord;
355:                                             }
356:                                         }
357:                                     }
358:                                 }
359:                             }
360:                         }
361:                     }
362:                 }
363:                 $records = array_merge($high, $medium);
364:                 $records = array_merge($records, $low);
365:     
366:                 $userRecords = array_merge($userHigh, $userMedium);
367:                 $userRecords = array_merge($userRecords, $userLow);
368:     
369:                 $records = array_merge($userRecords, $records);
370:     
371:                 $records = Record::convert($records, false);
372:                 if(count($records) == 1){
373:                     // Only one match, so go straight to it.
374:                     // 
375:                     // The record's corresponding model class must have
376:                     // X2LinkableBehavior for this to be possible.
377:                     if(!empty($records[0]['#recordUrl'])) {
378:                         $this->redirect($records[0]['#recordUrl']);
379:                     }
380:                 }
381:                 $dataProvider = new CArrayDataProvider($records, array(
382:                             'id' => 'id',
383:                             'pagination' => array(
384:                                 'pageSize' => Profile::getResultsPerPage(),
385:                             ),
386:                         ));
387:     
388:                 $this->render('search', array(
389:                     'records' => $records,
390:                     'dataProvider' => $dataProvider,
391:                     'term' => $term,
392:                 ));
393:             }else{
394:                 Yii::app()->user->setState('vcr-list', $term);
395:                 $_COOKIE['vcr-list'] = $term;
396:                 $tagQuery = "
397:                     SELECT * 
398:                     FROM x2_tags
399:                     WHERE tag=:tag
400:                     group BY tag, type, itemId";
401:                 $params = array (':tag' => $term);
402: 
403:                 // group by type and itemId to prevent display of duplicate tags
404:                 $sql = Yii::app()->db->createCommand ($tagQuery);
405:                 $totalItemCount = Yii::app()->db->createCommand ("
406:                     SELECT count(*)
407:                     FROM ($tagQuery) as t1;
408:                 ")->queryScalar ($params);
409: 
410:                 $results = new CSqlDataProvider ($sql, array (
411:                     'totalItemCount' => $totalItemCount,
412:                     'sort' => array(
413:                         'defaultOrder' => 'timestamp DESC',
414:                     ),
415:                     'pagination' => array(
416:                         'pageSize' => Profile::getResultsPerPage(),
417:                     ),
418:                     'params' => $params,
419:                 ));
420:                 $this->render('searchTags', array(
421:                     'tags' => $results,
422:                     'term' => $term,
423:                 ));
424:             }
425:         }
426:     }
427: 
428: }
429: 
430: ?>
431: 
X2CRM Documentation API documentation generated by ApiGen 2.8.0