]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - modules/Home/QuickSearch.php
Release 6.4.4
[Github/sugarcrm.git] / modules / Home / QuickSearch.php
1 <?php
2 /*********************************************************************************
3  * SugarCRM Community Edition is a customer relationship management program developed by
4  * SugarCRM, Inc. Copyright (C) 2004-2012 SugarCRM 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 SUGARCRM, SUGARCRM 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 SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
24  * SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.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  * SugarCRM" 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 SugarCRM".
35  ********************************************************************************/
36
37
38 require_once('include/SugarObjects/templates/person/Person.php');
39 require_once('include/MVC/SugarModule.php');
40
41 /**
42  * quicksearchQuery class, handles AJAX calls from quicksearch.js
43  *
44  * @copyright  2004-2007 SugarCRM Inc.
45  * @license    http://www.sugarcrm.com/crm/products/sugar-professional-eula.html  SugarCRM Professional End User License
46  * @since      Class available since Release 4.5.1
47  */
48 class quicksearchQuery
49 {
50     /**
51      * Condition operators
52      * @var string
53      */
54     const CONDITION_CONTAINS    = 'contains';
55     const CONDITION_LIKE_CUSTOM = 'like_custom';
56     const CONDITION_EQUAL       = 'equal';
57
58     protected $extra_where;
59
60     /**
61      * Query a module for a list of items
62      *
63      * @param array $args
64      * example for querying Account module with 'a':
65      * array ('modules' => array('Accounts'), // module to use
66      *        'field_list' => array('name', 'id'), // fields to select
67      *        'group' => 'or', // how the conditions should be combined
68      *        'conditions' => array(array( // array of where conditions to use
69      *                              'name' => 'name', // field
70      *                              'op' => 'like_custom', // operation
71      *                              'end' => '%', // end of the query
72      *                              'value' => 'a',  // query value
73      *                              )
74      *                        ),
75      *        'order' => 'name', // order by
76      *        'limit' => '30', // limit, number of records to return
77      *       )
78      * @return array list of elements returned
79      */
80     public function query($args)
81     {
82         $args = $this->prepareArguments($args);
83         $args = $this->updateQueryArguments($args);
84         $data = $this->getRawResults($args);
85
86         return $this->getFormattedJsonResults($data, $args);
87     }
88
89     /**
90      * get_contact_array
91      *
92      */
93     public function get_contact_array($args)
94     {
95         $args    = $this->prepareArguments($args);
96         $args    = $this->updateContactArrayArguments($args);
97         $data    = $this->getRawResults($args);
98         $results = $this->prepareResults($data, $args);
99
100         return $this->getFilteredJsonResults($results);
101     }
102
103     /**
104      * Returns the list of users, faster than using query method for Users module
105      *
106      * @param array $args arguments used to construct query, see query() for example
107      * @return array list of users returned
108      */
109     public function get_user_array($args)
110     {
111         $condition = $args['conditions'][0]['value'];
112         $results   = $this->getUserResults($condition);
113
114         return $this->getJsonEncodedData($results);
115     }
116
117
118     /**
119      * Returns search results from external API
120      *
121      * @param array $args
122      * @return array
123      */
124     public function externalApi($args)
125     {
126         require_once('include/externalAPI/ExternalAPIFactory.php');
127         $data = array();
128         try {
129             $api = ExternalAPIFactory::loadAPI($args['api']);
130             $data['fields']     = $api->searchDoc($_REQUEST['query']);
131             $data['totalCount'] = count($data['fields']);
132         } catch(Exception $ex) {
133             $GLOBALS['log']->error($ex->getMessage());
134         }
135
136         return $this->getJsonEncodedData($data);
137     }
138
139     /**
140      * Internal function to construct where clauses
141      *
142      * @param Object $focus
143      * @param array $args
144      * @return string
145      */
146     protected function constructWhere($focus, $args)
147     {
148         global $db, $locale, $current_user;
149
150         $table = $focus->getTableName();
151         if (!empty($table)) {
152             $table_prefix = $db->getValidDBName($table).".";
153         } else {
154             $table_prefix = '';
155         }
156         $conditionArray = array();
157
158         if (!is_array($args['conditions'])) {
159             $args['conditions'] = array();
160         }
161
162         foreach($args['conditions'] as $condition)
163         {
164             switch ($condition['op'])
165             {
166                 case self::CONDITION_CONTAINS:
167                     array_push(
168                         $conditionArray,
169                         sprintf(
170                             "%s like '%%%s%%'",
171                             $table_prefix . $db->getValidDBName($condition['name']),
172                             $db->quote($condition['value']
173                     )));
174                     break;
175
176                 case self::CONDITION_LIKE_CUSTOM:
177                     $like = '';
178                     if (!empty($condition['begin'])) {
179                         $like .= $db->quote($condition['begin']);
180                     }
181                     $like .= $db->quote($condition['value']);
182
183                     if (!empty($condition['end'])) {
184                         $like .= $db->quote($condition['end']);
185                     }
186
187                     if ($focus instanceof Person){
188                         $nameFormat = $locale->getLocaleFormatMacro($current_user);
189
190                         if (strpos($nameFormat,'l') > strpos($nameFormat,'f')) {
191                             array_push(
192                                 $conditionArray,
193                                 $db->concat($table, array('first_name','last_name')) . " like '$like'"
194                             );
195                         } else {
196                             array_push(
197                                 $conditionArray,
198                                 $db->concat($table, array('last_name','first_name')) . " like '$like'"
199                             );
200                         }
201                     }
202                     else {
203                         array_push(
204                             $conditionArray,
205                             $table_prefix . $db->getValidDBName($condition['name']) . sprintf(" like '%s'", $like)
206                         );
207                     }
208                     break;
209
210                 case self::CONDITION_EQUAL:
211                     if ($condition['value']) {
212                         array_push(
213                             $conditionArray,
214                             sprintf("(%s = '%s')", $db->getValidDBName($condition['name']), $db->quote($condition['value']))
215                             );
216                     }
217                     break;
218
219                 default:
220                     array_push(
221                         $conditionArray,
222                         $table_prefix.$db->getValidDBName($condition['name']) . sprintf(" like '%s%%'", $db->quote($condition['value']))
223                     );
224             }
225         }
226
227         $whereClause = sprintf('(%s)', implode(" {$args['group']} ", $conditionArray));
228         if(!empty($this->extra_where)) {
229             $whereClause .= " AND ({$this->extra_where})";
230         }
231
232         if ($table == 'users') {
233             $whereClause .= sprintf(" AND users.status='Active'");
234         }
235
236         return $whereClause;
237     }
238
239     /**
240      * Returns formatted data
241      *
242      * @param array $results
243      * @param array $args
244      * @return array
245      */
246     protected function formatResults($results, $args)
247     {
248         global $sugar_config;
249
250         $app_list_strings = null;
251         $data['totalCount'] = count($results);
252         $data['fields']     = array();
253
254         for ($i = 0; $i < count($results); $i++) {
255             $data['fields'][$i] = array();
256             $data['fields'][$i]['module'] = $results[$i]->object_name;
257
258             //C.L.: Bug 43395 - For Quicksearch, do not return values with salutation and title formatting
259             if($results[$i] instanceof Person)
260             {
261                 $results[$i]->createLocaleFormattedName = false;
262             }
263             $listData = $results[$i]->get_list_view_data();
264
265             foreach ($args['field_list'] as $field) {
266                 // handle enums
267                 if ((isset($results[$i]->field_name_map[$field]['type']) && $results[$i]->field_name_map[$field]['type'] == 'enum')
268                     || (isset($results[$i]->field_name_map[$field]['custom_type']) && $results[$i]->field_name_map[$field]['custom_type'] == 'enum')) {
269
270                     // get fields to match enum vals
271                     if(empty($app_list_strings)) {
272                         if(isset($_SESSION['authenticated_user_language']) && $_SESSION['authenticated_user_language'] != '') $current_language = $_SESSION['authenticated_user_language'];
273                         else $current_language = $sugar_config['default_language'];
274                         $app_list_strings = return_app_list_strings_language($current_language);
275                     }
276
277                     // match enum vals to text vals in language pack for return
278                     if(!empty($app_list_strings[$results[$i]->field_name_map[$field]['options']])) {
279                         $results[$i]->$field = $app_list_strings[$results[$i]->field_name_map[$field]['options']][$results[$i]->$field];
280                     }
281                 }
282
283
284                 if (isset($listData[$field])) {
285                     $data['fields'][$i][$field] = $listData[$field];
286                 } else if (isset($results[$i]->$field)) {
287                     $data['fields'][$i][$field] = $results[$i]->$field;
288                 } else {
289                     $data['fields'][$i][$field] = '';
290                 }
291             }
292         }
293
294         if (is_array($data['fields'])) {
295             foreach ($data['fields'] as $i => $recordIn) {
296                 if (!is_array($recordIn)) {
297                     continue;
298                 }
299
300                 foreach ($recordIn as $col => $dataIn) {
301                     if (!is_scalar($dataIn)) {
302                         continue;
303                     }
304
305                     $data['fields'][$i][$col] = html_entity_decode($dataIn, ENT_QUOTES, 'UTF-8');
306                 }
307             }
308         }
309
310         return $data;
311     }
312
313     /**
314      * Filter duplicate results from the list
315      *
316      * @param array $list
317      * @return  array
318      */
319     protected function filterResults($list)
320     {
321         $fieldsFiltered = array();
322         foreach ($list['fields'] as $field) {
323             $found = false;
324             foreach ($fieldsFiltered as $item) {
325                 if ($item === $field) {
326                     $found = true;
327                     break;
328                 }
329             }
330
331             if (!$found) {
332                 $fieldsFiltered[] = $field;
333             }
334         }
335
336         $list['totalCount'] = count($fieldsFiltered);
337         $list['fields']     = $fieldsFiltered;
338
339         return $list;
340     }
341
342     /**
343      * Returns raw search results. Filters should be applied later.
344      *
345      * @param array $args
346      * @param boolean $singleSelect
347      * @return array
348      */
349     protected function getRawResults($args, $singleSelect = false)
350     {
351         $orderBy = !empty($args['order']) ? $args['order'] : '';
352         $limit   = !empty($args['limit']) ? intval($args['limit']) : '';
353         $data    = array();
354
355         foreach ($args['modules'] as $module) {
356             $focus = SugarModule::get($module)->loadBean();
357
358             $orderBy = $focus->db->getValidDBName(($args['order_by_name'] && $focus instanceof Person && $args['order'] == 'name') ? 'last_name' : $orderBy);
359
360             if ($focus->ACLAccess('ListView', true)) {
361                 $where = $this->constructWhere($focus, $args);
362                 $data  = $this->updateData($data, $focus, $orderBy, $where, $limit, $singleSelect);
363             }
364         }
365
366         return $data;
367     }
368
369     /**
370      * Returns search results with all fixes applied
371      *
372      * @param array $data
373      * @param array $args
374      * @return array
375      */
376     protected function prepareResults($data, $args)
377     {
378         $results['totalCount'] = $count = count($data);
379         $results['fields']     = array();
380
381         for ($i = 0; $i < $count; $i++) {
382             $field = array();
383             $field['module'] = $data[$i]->object_name;
384
385             $field = $this->overrideContactId($field, $data[$i], $args);
386             $field = $this->updateContactName($field, $args);
387
388             $results['fields'][$i] = $this->prepareField($field, $args);
389         }
390
391         return $results;
392     }
393
394     /**
395      * Returns user search results
396      *
397      * @param string $condition
398      * @return array
399      */
400     protected function getUserResults($condition)
401     {
402         $users = $this->getUserArray($condition);
403
404         $results = array();
405         $results['totalCount'] = count($users);
406         $results['fields']     = array();
407
408         foreach ($users as $id => $name) {
409             array_push(
410                 $results['fields'],
411                 array(
412                     'id' => (string) $id,
413                     'user_name' => $name,
414                     'module' => 'Users'
415             ));
416         }
417
418         return $results;
419     }
420
421     /**
422      * Merges current module search results to given list and returns it
423      *
424      * @param array $data
425      * @param SugarBean $focus
426      * @param string $orderBy
427      * @param string $where
428      * @param string $limit
429      * @param boolean $singleSelect
430      * @return array
431      */
432     protected function updateData($data, $focus, $orderBy, $where, $limit, $singleSelect = false)
433     {
434         $result = $focus->get_list($orderBy, $where, 0, $limit, -1, 0, $singleSelect);
435
436         return array_merge($data, $result['list']);
437     }
438
439     /**
440      * Updates search result with proper contact name
441      *
442      * @param array $result
443      * @param array $args
444      * @return string
445      */
446     protected function updateContactName($result, $args)
447     {
448         global $locale;
449
450         $result[$args['field_list'][0]] = $locale->getLocaleFormattedName(
451             $result['first_name'],
452             $result['last_name'],
453             $result['salutation']
454         );
455
456         return $result;
457     }
458
459     /**
460      * Overrides contact_id and reports_to_id params (to 'id')
461      *
462      * @param array $result
463      * @param object $data
464      * @param array $args
465      * @return array
466      */
467     protected function overrideContactId($result, $data, $args)
468     {
469         foreach ($args['field_list'] as $field) {
470             $result[$field] = (preg_match('/reports_to_id$/s',$field)
471                                || preg_match('/contact_id$/s',$field))
472                 ? $data->id // "reports_to_id" to "id"
473                 : $data->$field;
474         }
475
476         return $result;
477     }
478
479     /**
480      * Returns prepared arguments. Should be redefined in child classes.
481      *
482      * @param array $arguments
483      * @return array
484      */
485     protected function prepareArguments($args)
486     {
487         global $sugar_config;
488
489         // override query limits
490         if ($sugar_config['list_max_entries_per_page'] < ($args['limit'] + 1)) {
491             $sugar_config['list_max_entries_per_page'] = ($args['limit'] + 1);
492         }
493
494         $defaults = array(
495             'order_by_name' => false,
496         );
497         $this->extra_where = '';
498
499         // Sanitize group
500         if(!empty($args['group'])  && strcasecmp($args['group'], 'and')) {
501             $args['group'] = 'AND';
502         } else {
503             $args['group'] = 'OR';
504         }
505
506         return array_merge($defaults, $args);
507     }
508
509     /**
510      * Returns prepared field array. Should be redefined in child classes.
511      *
512      * @param array $field
513      * @param array $args
514      * @return array
515      */
516     protected function prepareField($field, $args)
517     {
518         return $field;
519     }
520
521     /**
522      * Returns user array
523      *
524      * @param string $condition
525      * @return array
526      */
527     protected function getUserArray($condition)
528     {
529         return (showFullName())
530             // utils.php, if system is configured to show full name
531             ? getUserArrayFromFullName($condition, true)
532             : get_user_array(false, 'Active', '', false, $condition,' AND portal_only=0 ',false);
533     }
534
535     /**
536      * Returns additional where condition for non private teams
537      *
538      * @param array $args
539      * @return string
540      */
541     protected function getNonPrivateTeamsWhere($args)
542     {
543         global $db;
544
545         $where = sprintf(
546             "(teams.name like '%s%%' or teams.name_2 like '%s%%')",
547             $db->quote($args['conditions'][0]['value']),
548             $db->quote($args['conditions'][0]['value'])
549         );
550
551         $where .= (!empty($args['conditions'][1]) && $args['conditions'][1]['name'] == 'user_id')
552             ? sprintf(
553                 " AND teams.id in (select team_id from team_memberships where user_id = '%s')",
554                 $db->quote($args['conditions'][1]['value'])
555             )
556             : ' AND teams.private = 0';
557
558         return $where;
559     }
560
561     /**
562      * Returns JSON encoded data
563      *
564      * @param array $data
565      * @return string
566      */
567     protected function getJsonEncodedData($data)
568     {
569         $json = getJSONobj();
570
571         return $json->encodeReal($data);
572     }
573
574     /**
575      * Returns formatted JSON encoded search results
576      *
577      * @param array $args
578      * @param array $results
579      * @return string
580      */
581     protected function getFormattedJsonResults($results, $args)
582     {
583         $results = $this->formatResults($results, $args);
584
585         return $this->getJsonEncodedData($results);
586     }
587
588     /**
589      * Returns filtered JSON encoded search results
590      *
591      * @param array $results
592      * @return string
593      */
594     protected function getFilteredJsonResults($results)
595     {
596         $results = $this->filterResults($results);
597
598         return $this->getJsonEncodedData($results);
599     }
600
601     /**
602      * Returns updated arguments array
603      *
604      * @param array $args
605      * @return array
606      */
607     protected function updateQueryArguments($args)
608     {
609         $args['order_by_name'] = true;
610
611         return $args;
612     }
613
614     /**
615      * Returns updated arguments array for contact query
616      *
617      * @param array $args
618      * @return array
619      */
620     protected function updateContactArrayArguments($args)
621     {
622         return $args;
623     }
624
625     /**
626      * Returns updated arguments array for team query
627      *
628      * @param array $args
629      * @return array
630      */
631     protected function updateTeamArrayArguments($args)
632     {
633         $this->extra_where = $this->getNonPrivateTeamsWhere($args);
634         $args['modules'] = array('Teams');
635
636         return $args;
637     }
638 }